Introduction

The goal of this article is to illustrate the potential for cool applications when software is combined with custom hardware. I will provide a brief introduction to USB and how communication with a custom device can be used to create an annoying gadget.

Before reading this article let me give a little background as to why I would post what might be considered more of a hardware article on a software-centered website. My degree is in electrical engineering and my passion has always been with hardware but I've also enjoyed getting my hardware projects to interact with computers. Levent Saltuklaroglu's article on how to turn on LEDs with the parallel port inspired me. I saw that, even though this is predominantly a software programming site, people really enjoyed a little bit of the hardware side of things. So the objective is to take Levent's idea a little further by providing a USB example. NOTE: I consider myself a fairly capable programmer but expect to take some criticism as to how I implement the Windows programming side of things. Remember that my degree is not in Computer Science although I do enjoy programming quite a bit. I welcome any constructive feedback but ask that you be gentle.

The easy part was deciding to write an article involving hardware and software integration. The hard part was deciding what the hardware would be and how the software would interact with it. About 2 years ago I had the idea to create a USB prank device that would spoof mouse movements and keyboard strokes at predetermined intervals when the device was connected. Well, after about 2 minutes of Google time I found that my idea was not original but I decided to implement it anyway because I felt like I could add a few things to existing implementations and that it would be a good project to familiarize myself with USB. I hope you find the information useful and can have some fun playing with it. I decided to name the device HIDAche.

Prerequisites

I've tried to keep this project as simple as possible but because it involves a software and a hardware side, there are a few things you will need if you decide to recreate the project for yourself. If you just want to play with the software side of the project then you can use Visual C# Express Edition. If you are interested in the hardware side of things then you will also need the following:

Basic knowledge of schematics

Basic knowledge of circuit construction

ANSI C compiler (I use the Microchip C18 free Student Edition)

Microcontroller programmer (I use the PIC-MCP-USB)

The list above is for those who are interested in actually recreating the project. If you just want to take a look at the firmware code I recommend using Visual Studio but you can use any text file viewer.

USB Basics

The goal of this article is to provide information, by means of a fairly simple example, about how we can make a USB device that communicates with the computer and how we can create cool applications that utilize that communication. As this article is not meant to be an in-depth look at USB I will only cover the basics. If you want to learn more I suggest the following resources:

BeyondLogic - A great sight that sums up the basics of USB to get you going. A little dated.

Transfer Rates

USB currently comes in three general flavors; High Speed (480 Mbits/s max), Full Speed (12 Mbits/s max) and Low Speed (1.5 Mbits/s max). Of course the USB Implementor's Forum asks us not to use the terms High, Full, and Low speed but recommend "'USB' for slower speed products (1.5 Mb/s and 12Mb/s) and 'Hi-Speed USB' for high-speed products (480Mb/s)." USB 2.0 supports all three speeds. The USB 3.0 spec was recently released and supports a new "SuperSpeed" of 5Gbps. However, at the time of this writing, the spec is too new to really consider. The Microchip PIC 18F2455 microcontroller that I will be using in this project supports Full Speed and Low Speed transmission. We will set it up to use Full Speed communication as a USB 2.0 compatible device.

A great thing about USB is that the cabling is very simple. It’s not like a parallel or even a serial cable that requires you to keep track of a handful of wires. USB cables contain 4 wires; power, ground, and two data wires (D+ and D-). The USB Specification even dictates what color those wires should be so you can count on them being the same from cable to cable (look for the USB logo to be sure).

Pin Number

Color

Function

1

Red

Power (+5 volts)

2

White

D-

3

Green

D+

4

Black

Ground

Power

Power can be supplied to a USB device in several configurations. We can use the power and ground wires as a power source for a project if it is guaranteed to consume less than 100mA of current. This is the low power bus-powered mode. If we need any more power than that our device has to request it (up to 500 mA) from the host upon enumeration (high power bus-powered mode). If we need even more power then we have the option to make the device self-powered meaning it uses little or no power from the host. To keep things simple we will define our device as a low power bus-powered device which means we will get all of the juice we need from the computer and keep it under 100mA.

Communication

USB uses differential data signals on D- and D+ for its communication. Differential means it doesn’t define ones and zeroes with on and off voltage states or even positive and negative voltage states. A differential 1 is defined as D+ being 200mV greater than D-. A 0 is defined as D+ being at least 200mV less than D-. In this configuration it doesn’t matter what the voltage is on either line at a particular time but what the voltage 'difference is between the two lines at any given time. Seems difficult to implement but our microcontroller will take care of those low level details for us.

Device Drivers

One of the major stumbling blocks for USB hardware development is the device driver. A quick look at Toby Opferman's 6 part series on this website gives you an idea of how involved and dangerous (lots of blue screens) driver development can be. Fortunately, if we design things right, we can use drivers that others have already written. Windows has several preinstalled drivers for Operating System use from which we can build our application. This is where the idea of USB device classes comes into play. USB classes are a way to define a standard implementation for a group of devices that perform similar tasks. A few examples of classes that have their own definition under the USB Specification are:

Human Interface Devices (HID)

Audio Class

Communications Device Class (CDC)

Imaging Class

Mass Storage Class

The classes are so well defined that different devices created within them can often share a driver. For example, in Windows XP most devices that fall within the Audio Class will use usbaudio.sys. HID devices will use hidusb.sys. This will prove useful a little later.

Microcontrollers

The hardware can be the scariest part of a project like this but once you get a grasp on things it's really not too hard to implement. At the heart of many hardware projects nowadays is the microcontroller. It's basically a computer on a chip. In this project I used the PIC 18F2455 from Microchip but other microcontrollers from different manufacturers could have also worked. When I say “computer on a chip” I mean it. For just a few US dollars the 18F2455 has 23 possible I/O pins (great for turning things on like LEDs), an on-board serial port, Full Speed USB transceiver and can perform up to 12 million instructions per second. It can hold 24 kilobytes of program code, has 2KB of on-board RAM, and 256 bytes of EEPROM (the microcontroller's hard drive). These numbers seem small since the standard today is to talk in gigabytes but for small hardware projects it's more than enough. I know what you may be thinking; “Yeah, sounds great but who wants to program in assembly?” Well the good news is that you don't have to. You can do it in C (still not your favorite, I know, but better than assembly). I think what made Levent Saltuklaroglu's article so popular was that it allowed anybody to go home, splice open their parallel cable and start lighting up LEDs. I'll admit, microcontrollers are a little more complex but not impossible. Working with them is very similar to writing a C program in Visual Studio.

Bootloader

I could dedicate an entire article to hardware bootloaders. This article is long enough as it is so I will just give a brief description. Pulling the microcontroller off of your breadboard every time you need to reprogram it gets old really fast. You never get firmware right the first time so there will be plenty of reprograms with each project. To save time and frustration I use a bootloader. Basically, a bootloader is a segment of code that sits at the beginning of the microcontroller's program memory. On power up, that code checks for a button to be pressed. If it's pressed it goes into bootloader mode. If it's not pressed then it jumps to the user program block and begins execution. In bootloader mode, a GUI application can read a compiled hex file and send programming commands to the microcontroller via a USB connection. The bootloader interprets those commands and reprograms the device without the need of a hardware programmer. This allows us to use a microcontroller programmer to program the device a single time with the bootloader code. After that we can reprogram the device with our application code as many times as we like by using the USB connection and a simple GUI application.

The Good Stuff

Ok, that's enough background information. Let's get to the project and the code. So to create HIDAche we first need to know what functionality we will need from a hardware perspective. Pretty simple. We need to know the packet format for a HID mouse and keyboard so we can imitate them, USB communication to get that information to the computer, and a way to store our prank settings. Some features of the device I wanted to implement were as follows:

Be able to precisely specifiy the time between keystrokes or mouse movements down to the minute

Have just the mouse move, just the keyboard type, or both simultaneously

GUI to program prank settings

Install it in a thumb drive enclosure for stealth factor

Communication

To get our information to the computer we definitely want to use the principle of USB classes to simplify things. Another reason to choose this route is for stealth. If you use a custom driver the user will get the "Found New Hardware" dialog and have to complete a bunch of steps. That just won't do. For many of the classes described above, the OS will likely have drivers and autoinstall them like when you plug in a new USB keyboard. Since mouse and keyboard functions fall within the HID realm, I decided to go that route. By making the device HID compliant the OS should automatically recognize it and load up the drivers for it. Fortunately, Microchip has example firmware on their website for creating a mouse. I used this as a starting point for the firmware development. HID devices communicate by sending and receiving reports. This helps standardize all of the communication. This is what the definition of the report looks like in the firmware code:

Looks complicated but it's not too bad once you know how to read them (creating them is a whole different beast). The "Report Count" items are telling you the number of fields and the "Report Size" is the number of bits in each field. So for the buttons under Usage Page (Buttons) we see that there are three fields of 1 bit each for button state. This is followed by one field of 5 bits to pad the button information out to a byte. After that we define bytes for X and Y with "Usage (X)" and "Usage (Y)." They are both 8 bits wide. Following the mouse definition is the keyboard definition. Actual functioning reports are pretty difficult to put together and I'm no expert. I bring this up as a point. This sort of thing may seem daunting at first. It was for me but there is documentation all over the web for this sort of thing and if you have a little patience and determination you'll be up and running in no time.

Something important to note in the reports is the report ID for both the keyboard and mouse functionality. Our device isn't really a mouse or keyboard, it's a combination of the two. This is alright as long as the computer knows what kind of information we are sending in each packet. The ID in the report allows us to specify this. I chose a report ID of 75 for keyboard commands and 77 for mouse commands. These were arbitrary numbers and could have been different as long as they are not the same. What that does is, upon enumeration, the report tells the OS "When I want to send you a mouse packet it will have an ID of 77 and if I want to send a keyboard packet it will have an ID of 75." Now, when the computer gets a USB packet from our device with an ID of 77 it will know to translate the contents of the packet into the appropriate OS mouse movements or clicks. NOTE: The report ID is always the first byte in the packet.

With the reports all done we know exactly what our packets should look like going to the computer. The following tables show what the mouse and keyboard packets look like based on how they were defined in the reports.

Mouse

Bit 7

Bit 6

Bit 5

Bit 4

Bit 3

Bit 2

Bit 1

Bit 0

Byte 0

0x4D (Mouse report ID of 77)

Byte 1

Y overflow

X overflow

Y sign

X sign

1

Middle Button

Right Button

Left Button

Byte 1

X movement

Byte 2

Y movement

Keyboard

Value

Byte 0

0x4B (Keyboard report ID of 75)

Byte 1

Control Byte 0

Byte 2

Control Byte 1

Byte 3

HID Usage ID

Byte 4

HID Usage ID

Byte 5

HID Usage ID

Byte 6

HID Usage ID

Byte 7

HID Usage ID

Byte 8

HID Usage ID

Byte 9

HID Usage ID

The control bytes in the keyboard packet are used to "press" control keys like SHIFT, ALT, CTRL, etc. The rest of the bytes labeled HID Usage ID are for sending keystrokes. Since we are HID compliant we can't just send ASCII characters here. More on this later. That's really all there is to it. The operating system will interpret these packets and simulate mouse movements, clicks, and keystrokes appropriately. So how do we get values into our byte array to send to the computer?

Putting It All Together

So now we know how to communicate with the computer and we know what we want to communicate. We just put it together. But first, when we plug in the device it needs to know what to do. To accomplish this we'll store the prank settings in the microcontroller's EEPROM. This is like the hard drive of the PIC because data persists even when power is removed. I've decided to organize the prank information in the 256 bytes of EEPROM as follows:

The keystrokes can occupy the rest of the EEPROM after 0x0E. The DEVICE_STRUCT_LOC will contain a unique ID so our programming software knows that a HIDAche device has been connected. When the device is plugged in, the firmware will read all of that data from the EEPROM.

// Read in the hidache device info
// Read in the prank interval
ReadEEPROMData(INTERVAL_LOC, (char*)&prankInterval, 1);
// Read in the device mode
ReadEEPROMData(DEV_MODE_LOC, (char*)&deviceMode, 1);
// Read in the mouse mode
ReadEEPROMData(MOUSE_MODE_LOC, (char*)&mouseMode, 1);
// Read in the number of keystrokes for keyboard mode
ReadEEPROMData(KEYS_LENGTH_LOC, (char*)&phraseLength, 1);
// Now read in the phrase just once so we don't have to read it every time the
// prank interval hits
ReadEEPROMData(PHRASE_START_LOC, phraseArray, phraseLength);

Another feature we need is the ability to move the mouse or send keystrokes on a specified time interval. To implement the delay between mouse movements or keystrokes I used another handy feature of microcontrollers; an internal timer. You can look in the code for details but essentially I did some fine tuning and got the timer to trigger an interrupt every second. I use that interval to perform the correct actions on the minute interval specified when you program the device.

USB communication has very strict timings. To satisfy the demands of that communication we can't go very long in our firmware code without servicing the USB tasks. This is accomplished with a call to USBDeviceTasks(). This does things like check for bus activity and certain control packets. Since this has to be called quite frequently, the entire firmware runs in an infinite while loop. In that while loop we service the USB and then handle our prank tasks with a call to ProcessIO(). In ProcessIO() we check to see if the timer interrupt has been triggered and if the prank interval has expired. If it has we make a call to PerformEmulation() where we decide what to emulate based on our settings.

switch(deviceMode)
{ case MODE_MOUSE:
// For mouse only mode we can go ahead and set the keyboard
// finished variable up front.
IsKeyboardFinished = TRUE;
// Load the next value in the buffer
FillMouseBuffer();
break;
case MODE_KEYBOARD:
// For keyboard only mode we can go ahead and set the mouse
// finished variable up front;
IsMouseFinished = TRUE;
// Load the next value in the buffer
FillKeyboardBuffer();
break;
case MODE_BOTH:
// Switch back and forth between the two modes until they both
// complete
if(sendType == KEYSTROKE)
FillKeyboardBuffer();
else
FillMouseBuffer();
break;
}
// Send the buffer to the computer
TransmitBuffer();

To not make the article more lengthy than it already is I'll refer you to the code to see what happens in the fill methods rather than put the code here. For mouse mode I essentially populate the movement portions of the packet to simulate the particular movement setting. Keyboard mode is a little more complicated but not too much. As I stated earlier, we can't just send ASCII characters to the computer since the device is enumerating as a HID USB device. We have to send the HID Usage code for particular keys. You can get these from the USB spec. I store the keystrokes as ASCII characters in EEPROM and then just translate them on the fly in firmware.

KeyTable is defined in the file TranslateAsciiToHID.c and assists in the translation. Once our transmission buffer is filled we're ready to send the information off to the computer with a call to TransmitBuffer(). At that point we'll either see mouse movement or keyboard strokes. If you look at the firmware you'll see that there are a lot of files and code to support the USB protocol. That code was written by Microchip and while it is nice to understand what is going on in servicing USB tasks it is not necessary. For our purposes they expose some methods for us to call to transmit data and that's all we really need to know. We can now move the mouse or send keystrokes based on settings that we program into our device. If you're feeling overwhelmed it's understandable. It's a lot to take in at first but is really pretty straight forward after you've looked at the code a bit. Just remember that I did not have to write a lot of the code. All of the USB code was provided by Microchip (no sense in reinventing the wheel). I've tried to heavily comment my portions of the code and explain what is going on. If you have questions, feel free to post them or email me. Most of my code is in hidache.c, TranslateAsciiToHID.c, HardwareProfile.h, and usb_descriptors.c.

That pretty much does it for the hardware programming. I've tried to intelligently comment the code that I changed from Microchip's original mouse example so that it is easy to understand. The only thing left from a hardware perspective is the schematic. As you can see it is very simple.

The PIC can read settings out of its EEPROM and perform prank actions based on those settings. Now we need a handy way to allow the devious prankster to get his/her desired prank settings onto the device. That's where the software comes in.

GUI HIDAche Programmer

When I began working on the Windows side of things for this project I had just read Josh Smith's article Creating the Same Program in Windows Forms and WPF which is what led me to do things the way I did with data binding. I realize the implementation is not perfect and would love to hear constructive feedback if you have it. The idea behind the GUI programmer is quite simple; provide a method to get prank settings onto the HIDAche device. Since I am using a bootloader on the prank device an easy solution presented itself. I know that the bootloader has the ability to read and write to the EEPROM on the PIC which happens to be where the prank settings are stored. Since I have the source code for the bootloader I also know what the command packets need to look like to access the EEPROM. So the simple solution is to have the device enter the bootloader mode and read and write that information just as a bootloader GUI program would. Most of the code is very straight forward so I'll just focus on a few key points. Let's see how it works.

Device Connection

If you remember from above to enter bootload mode we simply hold down the device button and plug it in. So if the GUI programmer is open and we plug in our device a couple of things need to happen. First, we need to be able to detect the connection of a USB device, and second, we need to be able to determine if that device is our prank device. Well, there are plenty of articles on this site about how to detect USB device connections so I won't go too much into it here. Basically all we do is override the WndProc method in our form and register for USB device notifications with a call to RegisterForHIDDeviceNotifications() which is located in the USBComm library. That overriden function looks like this:

WM_DEVICECHANGE is the message that will be sent on a connection or removal of a USB device. So, if that is the message we receive we check the connection status to display whether a device is connected or disconnected. In our CheckConnectionStatus() method we verify that the VID/PID match what our device should have so we don't show a connected state when some random USB device is attached. If it is a HIDAche device we offer the option of reading in the current settings. Pretty simple. NOTE: I realize that there is a design flaw here. You'll notice that at the end of the overrided function there is a call to base.WndProc() which passes our message along. The fact that there is a MessageBox.Show() in the function means the message could be held up indefinitely which could cause a problem when connecting other USB hardware. Any suggestions on how to get around this would be appreciated. This is only a problem when programming our settings and isn't a problem when the device is attached in prank mode.

Databinding to a HIDacheDevice Object

In my best attempt to follow Josh Smith's example of databinding I use a BindingSource object to connect the elements of the HIDAcheControl to a HIDacheDevice object. It is a very straightforward way to separate the object from the UI. Every setting on the control is bound to an object member and will update it accordingly when you change things with the user interface. I really like this approach since it simplifies the code. When settings change we don't have to write code to actually change the values in the object, the binding takes care of it. Perhaps the most interesting part is binding the connection status image to the IsConnected public member. This took very little extra code. The following was added to the constructor of the HIDAcheControl.

All this does is force a call to ConvertIsConnectedToString() when the label binds. In that method we handle setting the correct text and the image based on the connection status.

USBComm Library

The last really interesting piece of this project is the software connection between the device and the computer used for programming prank settings. It's one thing to have the prank device send USB commands to the OS but it is another thing to get an application to send USB packets to our device. For this we use the USBComm library. I firmly believe in giving credit where credit is due. The majority of this library was written by Jan Axelson and can be downloaded from USB Central. Essentially the library simplifies the task of sending packets to HID devices like the prank device. I made a few changes and additions to the library to make things a little easier for this device like handling the fact that the packets come back left zero-padded in a 65 byte buffer. To handle the commands to read and write to the device EEPROM I created the DeviceCommand object which greatly simplifies the process of constructing the packets. As I mentioned above since I have the bootloader code I know what the commands are to perform a read and write of EEPROM. To create a packet to read 10 bytes from the EEPROM staring at address 0x0A the code would look like this.

// Library needs to know the VID/PID of our device. We set those in the firmware.
USBComm.HIDCommObject comm = new USBComm.HIDCommObject(0x003C, 0x04D8);
// Read command is 0x07
// Write command is 0x05
// The address of the DeviceCommand is 4 bytes. The F0 tells the bootloader code
// that we are going to write to EEPROM and the 0A at the end says we will start
// at address 0x0A.
USBComm.DeviceCommand command = new DeviceCommand(0x07, (Int32)0x00F0000A, 10,
newbyte[10]);
// Now we send it and get our response
comm.SendCommandAndGetResponseFromDevice(ref command);

Because we pass our DeviceCommand by reference SendCommandAndGetResponseFromDevice() returns the object to us with the requested data in the Data member of the DeviceCommand. That's pretty much it. The code is pretty easy to navigate so you can get more in-depth details by looking there. If you have any specific questions feel free to post them.

Points of Interest

Well, that wraps up my first CodeProject article. Hopefully it wasn't too painful to read. This is just one way to make use of hardware/software integration. The possibilities are only limited by your imagination. Once we have a way to communicate between the computer and a device we can do any number of things; turn on lights, display sensor readings, turn servos, etc. I'd like to write another article involving hardware/software integration with USB capability but will only do so if there is an interest so if you've enjoyed the article and would like to see more please let me know.

Comments and Discussions

Can I use other different frequencies of the crystal oscillators and need I use different capacitors accordingly? If I don't have the capacitors of exact the required capacity, how tolerant is it to the deviation of the capacitors?

You can use different crystal frequencies. See the datasheet for compatible frequencies for USB operation. If you do change it you will have to alter some of the config bits to ensure the USB module gets the proper clock timing. The Oscillator Configurations section of the datasheet will have more info. The 33pF values for the capacitor are pretty flexible. I've used as much as +/-10pF away from 33pF with success.

On pg 29 of the datasheet there is a table explaining the settings for a 24MHz input. You need to do a PLL divide by 6 so you'll change #pragma config PLLDIV=2 to PLLDIV=6 to get a 4MHz input to the PLL module. CPUDIV would be set in a manner to get your desired operating clock frequency. See the diagram on page 24 of the datasheet. If you leave the CPUDIV alone it will make your system clock be 32 MHz which should be fine.

I do believe I still have the source code for that somewhere. I'll look into that today and try to post it if I can find it. It involved some PIC firmware and a C# program but I'm not sure what state they are in as I haven't touched them in a couple of years. They weren't very polished so that's why I didn't post them originally. I'm working on a new bootloader that's not USB based but SD card based. If I can ever find the time to finish it I'll post an article on Codeproject about it.

What I'm really interested in is in what way you were inspired from the bootloader code to 'reprogram' the chip in circuit without needing a PIC programmer and without removing the chip from the circuit.

I need more details on this (And I believe I'm not alone in this).

Whether an article is too long is not simply judged by its length, but by the proportion of the amount of beef comparing to its physical length, IMO.

A revision of this article with more details (this does not imply that the current state of it is not good enough) or another new article presenting some prerequisites for this article would be both much appreciated.

I'm trying to recreate this project with components (they are on the way). And I believe this project is a pretty good starting point for any further SW/HW integration/collaboration.

Max,Unfortunately I do not have the bootloader code still in a working state to send to you. I had started making some drastic changes to the firmware and I wouldn't feel good sending it to you in a broken state. I'm currently working on a new version of the bootloader that I plan to put up on github. I'll also be posting on my blog about the process of creating it so I'd recommend checking there for updates. Once I finish it up I may create a polished article to post here on Codeproject but for now I'm still in the prototyping phase. Thanks for your interest.

Pulling the microcontroller off of your breadboard every time you need to reprogram it gets old really fast. You never get firmware right the first time so there will be plenty of reprograms with each project. To save time and frustration I use a bootloader. Basically, a bootloader is a segment of code that sits at the beginning of the microcontroller's program memory. On power up, that code checks for a button to be pressed. If it's pressed it goes into bootloader mode. If it's not pressed then it jumps to the user program block and begins execution. In bootloader mode, a GUI application can read a compiled hex file and send programming commands to the microcontroller via a USB connection. The bootloader interprets those commands and reprograms the device without the need of a hardware programmer. This allows us to use a microcontroller programmer to program the device a single time with the bootloader code. After that we can reprogram the device with our application code as many times as we like by using the USB connection and a simple GUI application.

That's it. The one additional thing you'll need if you don't have it is a programmer to get the firmware onto the PIC18F2455. If you don't have one I recommend the Pickit3 from Microchip. It's pretty affordable and will program any of the PICs they sell. Let me know if you have any other questions.

I'm quite a freshman regarding PIC stuff, I just want to know if I can replace the PIC chip mentioned in the article with other models, say, a PIC18F2550 or 4550? Are they also equivalent for the project mentioned in your another article FMBomb - A Beginner's Approach to Hardware Programming[^]?

Yes, the 2550 or 4550 will work for the HIDAche device. You may have to make minor tweaks to the configuration bits and PIN assignments but otherwise the code should port fine. The PIC used for the FMBomb is a much lighter weight chip but a 2550 or 4550 would work as well. They would just be overkill but if that's what you have they will certainly work. Again some tweaks to config bits and pin assignments would be necessary.

For the time being I have none of the mentioned chips. The reason why asked the question is obviously to determine if I could prepare a 2-in-1 chip for both projects, or even more, at the lowest cost and most convenience.

Yes, I was aware of-by guess-that the chip for FMBomb must be a much lighter weight one comparing the 2 others, as long as they are compatible.

I'll certainly try to figure out the tweaks needed before asking for your help.

Thanks for the suggestion. I don't think I ever took a picture of it and I've since removed it from my breadboard to make room for other projects. I'll have to reassemble it sometime and snap a picture and post it. Thanks.