Sunday, June 10, 2018

Scrolling Text On LCD with Arduino

Introduction

We want to program the Arduino so that it will scroll (possibly very long) text acroos an LCD screen.
The text shall be sent to the Arduino via the serial port
The text transmitted by the PC via the USB link is stored in the Arduino Memory and displayed as a scrolling text on the LCD display.
The major risk is to lose data in case of very long text since the Arduino internal serial port buffer is limited to 64 bytes

The Hardware

The hardware is the classical Interfacing of an Arduino UNO with a 2x16 HD44780 LCD module. Note that for the module I did use did include a current limiting resistor in the circuit backlight. This may not be the case for your module. So be careful. You may destroy the module if you don’t include an external resistance on a module that has no internal limiting resistance.  (see https://electronics.stackexchange.com/questions/212197/is-it-necessary-to-use-resistor-when-connecting-backlight-of-16x2-lcd-display for details).

For the NPN transistor. I did use a BC547 but any similar transistor (BC108, 2N222,…) may be used instead.

The software

The software is made of 2 parts:

The display part

The display part builds a 16 characters string containing the part of the buffer to print (if needed padded with white spaces) and display it with a lcd.print command. This has the advantage of not disturbing the other lines of the display.
Since the Arduino serial port input buffer is very small (64 bytes), the display routine is immediately stopped once bytes are received in the serial port input buffer. (note: the process could probably be optimized using interrupts but tests did show the program was working correctly without using interrupts

The communication Part

This routine simply fills a large (256 bytes) buffer with the data received on the serial port using the method Serial.readBytesUntil('\n',buffer,256);
The handshaking is done via an XON XOFF mechanism.

Full listing

/*
 LCD Serial ScrollPrint

MIT License

Copyright (c) 2018 Pierre Poliakoff

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

 */

// include the library code:
#include <LiquidCrystal.h>

//declare the hardware
LiquidCrystal lcd(2, 3, 4, 5, 6, 7);

int pinBacklight=9;

//const
//=====
const byte XOFF=19;
const byte XON=17;
//variables
//=========
char buffer[256];



void LcdPrintAndScroll(char *buffer,int len)
{
int offset =-16;
String screen="                "; //16 chars buffer string
while(Serial.available()==0)
  {
  lcd.setCursor(0,0);
  offset++;
  if(offset>len)
   {
    offset=-16;
   }
 
  for (int i=0; i <16 && Serial.available()==0 ;i++)
    {
      if((i+offset<len)&&(i+offset>=0)&&buffer[i+offset]>31)
       {
        screen[i]=buffer[i+offset];
       }
      else
      {
        screen[i]=' ';
      }
    }
  lcd.print(screen);
  unsigned long now=millis();
  while(millis()-now<250 && Serial.available()==0)
    {
      //do nothing
    }
  }
}

void setup() {
  // initialize Hardware
  pinMode(pinBacklight,OUTPUT);
  digitalWrite(pinBacklight,HIGH);
  Serial.begin(9600);

  // set up the LCD's number of columns and rows:
  lcd.begin(16, 2);

  lcd.print("Waiting for Data");
  lcd.setCursor(0,1);
  lcd.print("Scroll Text Demonstration");

}
//===========================================
void loop() {
int len;
// receive data
while (Serial.available()==0)
  {
   //Do nothing 
  }
len=Serial.readBytesUntil('\n',buffer,256);
Serial.write(XOFF); // pause the transmission
Serial.flush();
// insert herre any long computation on Buffer
Serial.write(XON);//reactivate the transmission
//Print Data
if (len>0)
  {
    LcdPrintAndScroll(buffer,len);
  }

}

Sending Data from the PC

We do not want to write a specific software on the PC to send the data. We  simply use the Windows Command line features:
On my PC the Arduino is visible as COM4 (check on you PC for the exact value)
We configure the com port communication via the following command

mode COM4 BAUD=9600 xon=on data=8


Note that each time you upload a new firmware version in the Arduino, you must reconfigure the com port with the above command
Then you can send the data to display via a simple copy command

copy mydata.txt com4:

if you prefer to enter the data directly with the keyboard, you can use

copy con: com4:

Press enter to display the text on the Arduino and CTRL-Z to exit.




The Final Result




Sunday, April 1, 2018

TTL Logic families compatibility

Logic families compatibility

Introduction

A few year ago, the logic gates were all using 5V and everything was clear. But today (2018) we see more and more 3.3V logic. The Raspberry PI, the Arduino M0, the Digilent Basys3, the Wemos D1 Mini have all 3.3V inputs But the classical Arduino UNO has 5V logic ports.

This WEMOS D1 MINI PRO is has 3.3V ports but the Arduino MEGA ADK has 5V ports

This means that when you are designing a circuit, you have to take care of the logic level you are using and when needed use level shifters that will convert the logic signal to the correct level.

Logic families

There are quite a few digital circuits families. The most common are the 74HCxxx, the 74HCTxxx and the very old 74LSxxx but there are many others.





The input level determine what voltage range is acceptable for a HIGH level and what voltage level is acceptable for a LOW level

For a 74HCT00 input, the LOW level is associated to a voltage between 0V and 0.8V and a HIGH level is associated to a voltage between 2V and 5V.

This means that voltage below 0V or above 5 V may damage the chip and any voltage between 0.8V and 2V is not a well defined logic signal.


To know if the output of a gate is compatible with the 74HCT family we must know if the gate output level fall within the acceptable values. However, this is not as simple as one would expect as the gate output voltage is dependent on the gate power supply voltage (i.e. the VCC value) and the current that the gate provide (the more current is drawn from the gate the lower the HIGH voltage shall become).

If we take a 74ALVC00 with VCC = 2.7V and an output current of 12mA we have the HIGH voltage = 2.2V and a LOW output voltage of 0.4V.

We see that the the 74ALVC00 output voltage HIGH (2.2V in the selected conditions) fits inside the 74HCT00 High voltage input range (between 2 and 5V) and that the 74ALVC00 output voltage LOW (0.4V in the selected conditions) fits inside the 74HCT00 LOW voltage input range (between 0 and 0.8V)



So Yes a 74ALVC00 with VCC2.7V can drive a 74HCT00 wit VCC=5V


What happens if we do the opposite

The 74ALCV LOW input voltage ranges is between 0 and 0.7V and the HIGH input voltage is between 2V and 3.6V. The 74HCT has an output voltage LOW between 0 and 0.44V and an output voltage HIGH of at least 0.44V



This means that contrarily to the previous case, here the gates are not compatible and connecting them that way could damage the 74ALVC00 gate.




This incompatibility is due to the fact that the 74ALVC is not 5V tolerant. If we had used a 74LVC instead (which is a 5V tolerant family)  it would have been OK.

Compatibility table

The table below was obtained by analyzing the datasheets of a NAND gate (7400 or equivalent) from each logic family and comparing the input and output voltage ranges.



To use the table select an output family (e.g. 74HCT: last line) it will indicate to what kind of gate input it can be connected. In the 74HCT example, any family except the 74AC,74HC (if VCC is 3.3V) ,and 74 ALVC.

As you can see in the table there is much more green than red which is a very good news since this means that many families are indeed compatible


input
PSU =3V
PSU=5V
Logic type

CMOS in 3V
TTL3V ONLY
CMOS in 3V 5V Tolerant
TTL3V ONLY 5VTolerant
CMOS in 5V
CMOS in 5V
old CMOS
TTL5V only
CMOS 5V only compatible TTL

Family names
AC

HC
ALVC
AHC

LV

VHC
LCX

LVC

LVX
AC

HC
AHC

LV

VHC
CD4xxx ALS

AS

F

LS

S
ACT

AHCT

HCT
output PSU =3V CMOS in 3V
AC

HC
Y
Y
y
Y
N
N
N
Y
Y
TTL3V ONLY
ALVC
Y
Y
Y
y
N
N
N
Y
Y
CMOS in 3V 5V Tolerant
AHC

LV

VHC
y
Y
Y
Y
N
N
N
Y
Y
TTL3V ONLY 5VTolerant
LCX

LVC

LVX
Y
y
Y
Y
N
N
N
Y
Y
PSU=5V
CMOS in 5V
AC

HC
N
N
Y
Y
Y
y
Y
Y
Y
CMOS in 5V
AHC

LV

VHC
N
N
Y
Y
Y
y
Y
Y
Y
old CMOS
CD4xxx
N
N
Y
Y
Y
Y
Y
Y
Y
TTL5V only
ALS

AS

F

LS

S
N
N
Y
Y
N
N
N
Y
Y
CMOS 5V only compatible TTL
ACT

AHCT

HCT
N N Y Y Y Y Y Y Y

Adapting levels

As can be seen in the table above, there 3 types of incompatibilities

TTL 5V driving CMOS 5V

This is a very old incompatibility that dates back to 1970!

e.g. a 74LS driving a 74HC or a 74F driving a CD4000

The TTL 5V gates have an HIGH voltage level that is not high enough to be identified as a HIGH level by the CMOS gate

One simple solution is to interface both systems using a 74HCT gate. indeed the input of a 74HCT gate is compatible with TTL 5V and its output is compatible CMOS




TTL 3.3V driving CMOS 5V

This is the same incompatibility as for TTL 5V driving CMOS 5V: the TTL 3V output voltage HIGH is not HIGH enough. Here also, using a 74HCT is a good workaround.



5V Logic driving 3V3 Non 5V tolerant logic

This is the problem: that occurs when you want to drive a Raspberry PI input with a 5V signal. You risk to damage  the 3V3 raspberry PI chip with the 5V output. The workaround is to use 5V tolerant 3V3 logic gate.e.g. a 74LVC gate as interface.


DISCLAIMER:

 The compatibility table was made by analyzing the datasheet of one manufacturer (most of the times Texas Instruments and sometimes ON SEMI or other manufacturers) .Some extrapolation was needed when the datasheet did not contain the value of VCC (3.3V / 5V) or the gate output current (12mA). This means that the table is for reference only and I cannot guarantee it does not contain some errors or inaccuracies. This table is no replacement for consulting the manufacturer datasheet

Sunday, April 30, 2017

Using the PI Cobbler on an Arduino

Introduction


The PI Cobbler (made by Adafruit) is a great tool that is used to connect a Raspberry PI to a bread board.
PI Cobbler

The Arduino Uno board on the other hand is not so straight forward to connect to a breadboard. You often end up with a mess of many wires
Arduino Uno requires individual wires towards the breadboard
An alternative is to use an Arduino Nano that fits directly on the breadboard
Arduino Nano fits directly on Breadboard


It would be great to be able to connect a flat cable directly to the Arduino UNO. So let's build an adapter UNO -> Flat cable

Assembly instructions

Needed stuff

You will need
  1. A PI Cobbler
  2. A 2x40 Positions Header, Breakaway Connector 0.100" (2.54mm) Through Hole
  3. An Arduino Uno
  4. An Arduino Protoshield (I use the one from Adafruit) 
  5. Some cabling wire
  6. Some solder
Update on May 2 2017:
STOP DON'T USE THE ADAFRUIT PI COBBLER!! it is only intended for the Raspberry PI. It contains internal wiring that connect all the Raspberry PI GND together this may damage your Arduino and / or prevent your project from working 

Monday, January 2, 2017

Creating a WASD joystick with an Arduino Esplora

Introduction

The classic WASD game keys are very popular. But if this key layout is nice on QWERTY keyboard, it is not so logical on an AZERTY keyboard.


With such layout you cannot make great score at any game.
The classic solution is to configure the keyboard as a US QWERTY keyboard for the duration of the game. But that's not a very satisfactory solution. What we need is a joystick that behaves like the WASD keys.

The Arduino Esplora board

The Arduino Esplora board is an Arduino Leonardo board that implement a joystick.
This is exactly what we need.

 The Leonardo (and the Esplora) board can behave like an HID device. This means that they can be programmed to behave as an USB mouse or as an USB  keyboard by the computer.

Now all we need is to program it so that the board joystick generates the WSAD key press.

The program

Enrico Gueli has written a program called "EsploraKart" (www.arduino.cc/en/Tutorial/EsploraKart) that does exactly what we want except that he has used the arrow keys instead of the WSAD keys.
Now lets modify his program to get the WSAD keys mapping:

/*
 esploraWASD
 This is a sketch is a modification of "EsploraKart" written by Enrico Gueli
 The joystick will send the WASD keys and
 the switches will send "space"
*/
#include <Esplora.h>
#include <Keyboard.h>
boolean buttonStates[8];
const byte buttons[] = {
  JOYSTICK_DOWN,
  JOYSTICK_LEFT,
  JOYSTICK_UP,
  JOYSTICK_RIGHT,
 
  SWITCH_RIGHT,
  SWITCH_LEFT,
  SWITCH_UP,
  SWITCH_DOWN
};
const char keystrokes[] = {
  's',
  'a',
  'w',
  'd',
  ' ',
  ' ',
  ' ',
  ' '

};
void setup() {
  Keyboard.begin();
}
void loop() {
  for (byte thisButton = 0; thisButton < 8; thisButton++) {
    boolean lastState = buttonStates[thisButton];
    boolean newState = Esplora.readButton(buttons[thisButton]);
    if (lastState != newState) { // Something changed!
      if (newState == PRESSED) {
        Keyboard.press(keystrokes[thisButton]);
      }
      else if (newState == RELEASED) {
        Keyboard.release(keystrokes[thisButton]);
      }
    }
    buttonStates[thisButton] = newState;
  }
  delay(50);
}

I have removed the comments and marked in red the modified lines

 Return of the AZERTY problem

The above program will work great if you have a QWERTY (or a German QWERTZ) keyboard, but strangely, it does not work with a computer configured with an AZERTY keyboard.

On a AZERTY computer the Esplora board outputs are  ZQSD instead of WASD.
If you look at the position of the ZQSD keys on an AZERTY keyboard you will notice that they are at the same location as the WASD keys on a QWERTY keyboard.

This is a limitation of the Arduino Keyboard library: this library allows to simulate a keyboard with a Leonardo or an Esplora board but the simulated keyboard is expecting that the computer uses a US QWERTY keyboard layout.

This origin of this restriction comes from the HID protocol: It specifies a key location on the keyboard and it is the computer responsibility to map this key location to a key symbol (or a special key).

I should have tried to adapt the Arduino Keyboard library to support multiple keyboard layouts. But I was too lazy ( this guy did something like that www.zem.fr). I simply have mapped the AZERTY keys in my program. This makes the program only usable on an AZERTY PC.

const char keystrokes[] = {
  's',//s
  'q',//a
  'z',//w
  'd',//d
  ' ',
  ' ',
  ' ',
  ' '
};

With this mapping the Esplora board is behaving like a WASD joystick. This is what I needed.

Sunday, December 18, 2016

Using mouse events on an HTML 5 Canvas in typescript

In this tutorial, I will show how to add a mouse event handler to an HTML 5 canvas using typescript.

Acknowledgements: Writing this would not have been possible without the excellent documentation I have found in http://www.typescriptgames.com/ 

The sample document:

To demonstrate the use of the mouse on a canvas, I have created a very simple page:


This page is a primitive drawing program that just draws a line on the canvas till the point where the mouse was clicked.

The html is below:


<!doctype html>
<html>
<head>
<title>Demonstration of Canvas and Typescript</title>
<link rel="stylesheet" href="canvas.css">
</head>
<body>
<header>
<h1>Mouse events on a canvas using typescript</h1>
</header>
<aside>
<h2>
Instructions
</h2>
<p>
Click on the canvas to draw a line from the last point to the point where you have clicked The first point is at the canvas center.
</p>
<p>
note: the canvas is cleared on resize.
</p>
</aside>
<canvas id="canvas">This is the canvas</canvas>
<script src=canvas.js></script>
</body>
</html>
And this is the CSS


h1{
    margin: 10px;
}
h2{
    margin: 0px;
}
aside {
    float: left;
    border: 1px solid black;
    padding: 10px;
    margin: 20px;
    width: 20%;
}
aside p{
    font-family: Arial, Helvetica, sans-serif;
}
#canvas {
    float: left;
    background: lightgray;
    border: 1px solid black;
    margin: 20px;
    width: 65%;
}

Handling the mouse event

Since we are using typescript, It is a good opportunity to make use of OOP. So let's create a class named canvasDraw that will handle all the operations related to drawing.

This class shall include an event handler that will receive the "mousedown" events
class CanvasDraw {
    canvas: HTMLCanvasElement;
    context: CanvasRenderingContext2D;
    private x: number;
    private y: number;
    public constructor() {
        this.canvas = <HTMLCanvasElement>document.getElementById("canvas");
        this.context = this.canvas.getContext("2d");
        this.canvas.addEventListener("mousedown", this.click, false);
    }
    public click = (event: MouseEvent): void => {
        this.context.beginPath();
        this.context.moveTo(this.x, this.y);
        let rect = this.canvas.getBoundingClientRect();
        this.x = (event.clientX - rect.left)/rect.width*this.canvas.width;
        this.y = (event.clientY - rect.top)/rect.height*this.canvas.height;
        this.context.lineTo(this.x, this.y);
        this.context.stroke();
        event.preventDefault();
    }
}
let canvasDraw: CanvasDraw = new CanvasDraw();

Syntax to declare the event handler:

We assign a lambda function to an attribute of the class:

 public click = (event: MouseEvent): void => { ... }

and we use this attribute to as a mousedown event listenr

this.canvas.addEventListener("mousedown", this.click, false);

This allows us to define the full code inside the class

Finding the clicked x/y position inside the canvas

The mouse event attributes clientX and ClientY cannot be used directly.

They need to be offset by the client top/left position and must be adapted by the canvas  scaling.

        let rect = this.canvas.getBoundingClientRect();
        x = (event.clientX - rect.left)/rect.width*this.canvas.width;
        y = (event.clientY - rect.top)/rect.height*this.canvas.height;

The canvas scaling problem

 If you try the above program (don't forget to compile the typescript into JavaScript) You will see that the result is not as good as expected. This is because the HTML 5 canvas maintain a distinction between the canvas size (i.e. the image drawn) and the displayed size.

By default the canvas size is 300px x 150 px but the displayed size is determined by the css. This means that most probably the displayed size will be much larger than the canvas drawing size leading to a "zoomed" image where all the pixels are visible and blurry.




To avoid this we should align the canvas size on the display size. But doing this is not straight forward.

Aligning canvas size on display size

To do this we use the following code

        this.canvas.height = this.canvas.offsetHeight;
        this.canvas.width = this.canvas.offsetWidth;

This works great but we need to find a way to call it at program startup  and each time the canvas is resized.

The resize event

There is no resize event on canvas in HTML 5 so we will have to use the event associated with the window object instead of the canvas object

class CanvasDraw {
    canvas: HTMLCanvasElement;
    context: CanvasRenderingContext2D;
    private x: number;
    private y: number;
    public constructor() {
        this.canvas = <HTMLCanvasElement>document.getElementById("canvas");
        this.context = this.canvas.getContext("2d");
        this.canvas.addEventListener("mousedown", this.click, false);
        //note resize event cannot be attached to the canvas
        window.addEventListener("resize", this.resize, false);
        this.resize(null);
    }
    public click = (event: MouseEvent): void => {
...
    }
    public resize = (event: UIEvent): void => {
        this.canvas.height = this.canvas.offsetHeight;
        this.canvas.width = this.canvas.offsetWidth;
    }
}

Note the call to resize at the end of the constructor that ensure that the initial canvas drawing area size is correct at the application startup

Important note: on most browser the canvas is cleared after resize but this behavior is not guaranteed. To avoid compatibility problems  we always clear the canvas with
 this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);

The Complete program

Just for completeness and if you want to try it, this is the full listing of canvas.ts

class CanvasDraw {
    canvas: HTMLCanvasElement;
    context: CanvasRenderingContext2D;
    private x: number;
    private y: number;
    public constructor() {
        this.canvas = <HTMLCanvasElement>document.getElementById("canvas");
        this.context = this.canvas.getContext("2d");
        this.canvas.addEventListener("mousedown", this.click, false);
        //note resize event cannot be attached to the canvas
        window.addEventListener("resize", this.resize, false);
        this.resize(null);
    }
    public click = (event: MouseEvent): void => {
        this.context.beginPath();
        this.context.moveTo(this.x, this.y);
        let rect = this.canvas.getBoundingClientRect();
        this.x = (event.clientX - rect.left);///rect.width*this.canvas.width;
        this.y = (event.clientY - rect.top);///rect.height*this.canvas.height;
        this.context.lineTo(this.x, this.y);
        this.context.stroke();
        event.preventDefault();
    }
    public resize = (event: UIEvent): void => {
        //the problem is that we must align canvas resolution on CSS set display size
        // otherwise there will be a zoom at display that will blur the lines
        this.canvas.height = this.canvas.offsetHeight;
        this.canvas.width = this.canvas.offsetWidth;
        this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
        this.x = this.canvas.width / 2;
        this.y = this.canvas.height / 2;
    }
}
let canvasDraw: CanvasDraw = new CanvasDraw();

Sunday, June 26, 2016

Redirecting stdout and stderr under Windows

Introduction

A console program (i.e. a program that is executed from the command line) can write its output on two separate streams: stdout and stderr. Theoretically stdout is used to display the program results and stderr is used to write error messages.

In a normal execution text written on both the stdout and the stderr isdisplayed on the console.

Lets try with an example. The following simplistic C# code writes a message on stdout and another message in stderr.



using System;
namespace TestStdout
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.Error.WriteLine("STDERR message");
            Console.Out.WriteLine("STDOUT message");
        }
    }
}


If we execute this program on the windows command line we get



D:\test>TestStdout.exe
STDERR message
STDOUT message

D:\test>

Redirection

The redirection of stdout and stderr is well known by Linux users but strangely not so much popular for Windows users. However, it works (nearly) the same way with both operating systems

Redirect stdout to a file

To redirect the program output (stdout) to a file, use the operator >



D:\test>TestStdout.exe >out.txt
STDERR message

D:\test>type out.txt
STDOUT message

D:\test>

To get rid of stdout you may redirect it to a special file called NUL that throws away anything sent to it. (note that in Linux it is called /dev/null).



D:\test>TestStdout.exe >NUL
STDERR message

D:\test>


Redirect stderr to a file

To redirect the stderr you use the operator 2>



D:\test>TestStdout.exe 2>err.txt
STDOUT message

D:\test>type err.txt
STDERR message

D:\test>


You may also redirect stderr to NUL if you want to get rid of it



D:\test>TestStdout.exe  2>NUL
STDOUT message


D:\test>

Redirect both stdout and stderr to a file

You may combine > and 2> to redirect both stdout and stderr



D:\test>TestStdout.exe >out.txt 2>err.txt


D:\test>

It is also possible to redirect both stdout and stderr to the same file by using 2>&1 to first redirect stderr to stdout (&1 is in fact stdout) and redirect stdout to a file.



D:\test>TestStdout.exe >OutAndErr.txt 2>&1


D:\test>type OutAndErr.txt
STDERR message
STDOUT message


D:\test>

Redirecting to the clipboard

On Windows sytsems, it is possible to redirect directly to the clipboard instead of redirecting to a file (thank yo to Scott Hanselman for this trick ) by using | clip

You can redirect stdout to the clipboard



D:\test>TestStdout.exe |clip
STDERR message


D:\test>

You can redirect both stdout and stderr to the clipboard by first redirecting stderr to stdout



D:\test>TestStdout.exe 2>&1 |clip


D:\test>

You can redirect only stderr to the clipboard

But this is tricky: you have to redirect stderr to stdout and stdout  to null and pipe to clip. Warning swapping the operations does not get the correct result.


D:\test>TestStdout.exe 2>&1 >NUL |clip


D:\test>