Hi Microsoft team I have a Arduino connected Joy Stick that Writes X and Y cordinate data to the Serial Monitor on Joy Stick Move , but when I read that data from a Windows GUI app in order to get it to draw on the windows GUI APP nothing happens
Windows APP:
#define _CRT_SECURE_NO_WARNINGS
#include <winsock2.h>
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <objidl.h>
#include <gdiplus.h>
using namespace Gdiplus;
#pragma comment (lib,"Gdiplus.lib")
#define COM_PORT "\\\\.\\COM7"
#define BUF_SIZE 256
// Global variables
HANDLE hComm;
HWND g_hWnd = NULL;
int g_currentX = 100;
int g_currentY = 100;
// Data structures for IOCP
typedef struct _PER_IO_DATA {
OVERLAPPED Overlapped;
WSABUF DataBuf;
CHAR Buffer[BUF_SIZE];
DWORD BytesRECV;
DWORD BytesSEND;
} PER_IO_DATA, * LPPER_IO_DATA;
typedef struct _PER_HANDLE_DATA {
HANDLE FileHandle;
char lineBuffer[BUF_SIZE];
int linePos;
} PER_HANDLE_DATA, * LPPER_HANDLE_DATA;
// --------------------------------------------------------------
void process_line(const char* s)
{
int dx, dy;
if (sscanf(s, "%d,%d", &dx, &dy) == 2) {
// Update drawing position
g_currentX += dx;
g_currentY += dy;
// Keep within reasonable bounds
if (g_currentX < 0) g_currentX = 0;
if (g_currentY < 0) g_currentY = 0;
if (g_currentX > 800) g_currentX = 800;
if (g_currentY > 600) g_currentY = 600;
// Trigger window redraw
if (g_hWnd) {
InvalidateRect(g_hWnd, NULL, TRUE);
}
}
}
// --------------------------------------------------------------
DWORD WINAPI WorkerThread(LPVOID lpParam) {
HANDLE hCompletionPort = (HANDLE)lpParam;
DWORD BytesTransferred = 0;
LPPER_IO_DATA lpPerIoData = NULL;
LPPER_HANDLE_DATA lpPerHandleData = NULL;
DWORD dwError = 0;
while (TRUE) {
// Wait for I/O completion
BOOL bRet = GetQueuedCompletionStatus(
hCompletionPort,
&BytesTransferred,
(PULONG_PTR)&lpPerHandleData,
(LPOVERLAPPED*)&lpPerIoData,
INFINITE
);
if (!bRet) {
dwError = GetLastError();
if (lpPerIoData == NULL) {
// Error occurred without a valid OVERLAPPED structure
break;
}
}
// Check for shutdown signal
if (BytesTransferred == 0 && lpPerHandleData == NULL) {
break;
}
// Process received data
if (BytesTransferred > 0 && lpPerHandleData != NULL) {
// Process each character received
for (DWORD i = 0; i < BytesTransferred; i++) {
char ch = lpPerIoData->Buffer[i];
if (ch == '\r' || ch == '\n') {
if (lpPerHandleData->linePos > 0) {
lpPerHandleData->lineBuffer[lpPerHandleData->linePos] = '\0';
process_line(lpPerHandleData->lineBuffer);
lpPerHandleData->linePos = 0;
}
}
else if (lpPerHandleData->linePos < BUF_SIZE - 1) {
lpPerHandleData->lineBuffer[lpPerHandleData->linePos++] = ch;
}
}
// Issue another read
ZeroMemory(&(lpPerIoData->Overlapped), sizeof(OVERLAPPED));
lpPerIoData->DataBuf.len = BUF_SIZE;
lpPerIoData->DataBuf.buf = lpPerIoData->Buffer;
DWORD Flags = 0;
DWORD RecvBytes = 0;
if (!ReadFile(
lpPerHandleData->FileHandle,
lpPerIoData->Buffer,
BUF_SIZE,
&RecvBytes,
&(lpPerIoData->Overlapped)
)) {
dwError = GetLastError();
if (dwError != ERROR_IO_PENDING) {
printf("ReadFile failed: %d\n", dwError);
free(lpPerIoData);
}
}
}
}
return 0;
}
// --------------------------------------------------------------
int InitializeSerialPort(HANDLE hCompletionPort)
{
// Open port
hComm = CreateFileA(COM_PORT, GENERIC_READ | GENERIC_WRITE,
0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
if (hComm == INVALID_HANDLE_VALUE) {
printf("Cannot open %s (Error: %d)\n", COM_PORT, GetLastError());
return 1;
}
// Allocate per-handle data
LPPER_HANDLE_DATA lpPerHandleData = (LPPER_HANDLE_DATA)malloc(sizeof(PER_HANDLE_DATA));
if (lpPerHandleData == NULL) {
CloseHandle(hComm);
return 1;
}
lpPerHandleData->FileHandle = hComm;
lpPerHandleData->linePos = 0;
// Associate with completion port
if (CreateIoCompletionPort(hComm, hCompletionPort, (ULONG_PTR)lpPerHandleData, 0) == NULL) {
printf("CreateIoCompletionPort failed: %d\n", GetLastError());
free(lpPerHandleData);
CloseHandle(hComm);
return 1;
}
// Configure serial port: 115200, 8N1
DCB dcb = { 0 };
dcb.DCBlength = sizeof(dcb);
GetCommState(hComm, &dcb);
dcb.BaudRate = 115200;
dcb.ByteSize = 8;
dcb.Parity = NOPARITY;
dcb.StopBits = ONESTOPBIT;
SetCommState(hComm, &dcb);
// Set timeouts for overlapped I/O
COMMTIMEOUTS to = { 0 };
to.ReadIntervalTimeout = MAXDWORD;
to.ReadTotalTimeoutConstant = 0;
SetCommTimeouts(hComm, &to);
PurgeComm(hComm, PURGE_RXCLEAR | PURGE_TXCLEAR);
// Allocate per-I/O data and start first read
LPPER_IO_DATA lpPerIoData = (LPPER_IO_DATA)malloc(sizeof(PER_IO_DATA));
if (lpPerIoData == NULL) {
free(lpPerHandleData);
CloseHandle(hComm);
return 1;
}
ZeroMemory(&(lpPerIoData->Overlapped), sizeof(OVERLAPPED));
lpPerIoData->DataBuf.len = BUF_SIZE;
lpPerIoData->DataBuf.buf = lpPerIoData->Buffer;
DWORD Flags = 0;
DWORD RecvBytes = 0;
if (!ReadFile(hComm, lpPerIoData->Buffer, BUF_SIZE, &RecvBytes, &(lpPerIoData->Overlapped))) {
DWORD dwError = GetLastError();
if (dwError != ERROR_IO_PENDING) {
printf("Initial ReadFile failed: %d\n", dwError);
free(lpPerIoData);
free(lpPerHandleData);
CloseHandle(hComm);
return 1;
}
}
printf("Joystick -> Drawing ready (COM7 @ 115200)\n");
return 0;
}
// --------------------------------------------------------------
VOID OnPaint(HDC hdc)
{
Graphics graphics(hdc);
// Draw crosshair at current position
Pen penRed(Color(255, 255, 0, 0), 2);
Pen penBlue(Color(255, 0, 0, 255), 1);
// Draw lines showing joystick position
graphics.DrawLine(&penRed, g_currentX - 20, g_currentY, g_currentX + 20, g_currentY);
graphics.DrawLine(&penRed, g_currentX, g_currentY - 20, g_currentX, g_currentY + 20);
// Draw circle
graphics.DrawEllipse(&penBlue, g_currentX - 10, g_currentY - 10, 20, 20);
// Draw some reference lines
Pen penGray(Color(255, 200, 200, 200));
graphics.DrawLine(&penGray, 0, 100, 800, 100);
graphics.DrawLine(&penGray, 0, 200, 800, 200);
graphics.DrawLine(&penGray, 0, 300, 800, 300);
graphics.DrawLine(&penGray, 100, 0, 100, 600);
graphics.DrawLine(&penGray, 200, 0, 200, 600);
graphics.DrawLine(&penGray, 300, 0, 300, 600);
}
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, PSTR, INT iCmdShow)
{
HWND hWnd;
MSG msg;
WNDCLASS wndClass;
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
// Initialize GDI+
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
wndClass.style = CS_HREDRAW | CS_VREDRAW;
wndClass.lpfnWndProc = WndProc;
wndClass.cbClsExtra = 0;
wndClass.cbWndExtra = 0;
wndClass.hInstance = hInstance;
wndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndClass.lpszMenuName = NULL;
wndClass.lpszClassName = TEXT("JoystickDrawing");
RegisterClass(&wndClass);
hWnd = CreateWindow(
TEXT("JoystickDrawing"),
TEXT("Joystick Drawing Application"),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
800,
600,
NULL,
NULL,
hInstance,
NULL);
g_hWnd = hWnd;
// Create I/O Completion Port
HANDLE hCompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 1);
if (hCompletionPort == NULL) {
printf("CreateIoCompletionPort failed: %d\n", GetLastError());
return 1;
}
// Initialize serial port
if (InitializeSerialPort(hCompletionPort) != 0) {
CloseHandle(hCompletionPort);
return 1;
}
// Create worker thread
HANDLE hThread = CreateThread(NULL, 0, WorkerThread, hCompletionPort, 0, NULL);
if (hThread == NULL) {
printf("CreateThread failed: %d\n", GetLastError());
CloseHandle(hComm);
CloseHandle(hCompletionPort);
return 1;
}
ShowWindow(hWnd, iCmdShow);
UpdateWindow(hWnd);
// Message loop
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
// Cleanup
CloseHandle(hComm);
PostQueuedCompletionStatus(hCompletionPort, 0, 0, NULL);
WaitForSingleObject(hThread, INFINITE);
CloseHandle(hThread);
CloseHandle(hCompletionPort);
GdiplusShutdown(gdiplusToken);
return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
switch (message)
{
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
OnPaint(hdc);
EndPaint(hWnd, &ps);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
Arduino JoyStick Code:
// Arduino Joystick to Serial Communication
// Connect joystick VRx to A0, VRy to A1
// This sends dx,dy movements compatible with the Windows drawing app
const int VRX_PIN = A0; // Analog pin for X axis
const int VRY_PIN = A1; // Analog pin for Y axis
const int SW_PIN = 2; // Digital pin for joystick button (optional)
// Center calibration values (adjust these based on your joystick)
int centerX = 512;
int centerY = 512;
// Deadzone to prevent drift when joystick is centered
const int DEADZONE = 50;
// Sensitivity multiplier (adjust for faster/slower movement)
const float SENSITIVITY = 0.3;
// Update rate in milliseconds
const int UPDATE_INTERVAL = 20; // 50Hz update rate
unsigned long lastUpdate = 0;
void setup() {
Serial.begin(115200);
pinMode(SW_PIN, INPUT_PULLUP); // Button pin with pullup
// Calibrate center position on startup
delay(500); // Wait for stable readings
long sumX = 0, sumY = 0;
const int samples = 100;
for (int i = 0; i < samples; i++) {
sumX += analogRead(VRX_PIN);
sumY += analogRead(VRY_PIN);
delay(5);
}
centerX = sumX / samples;
centerY = sumY / samples;
Serial.println("0,0"); // Initial position
Serial.print("# Calibrated - Center X: ");
Serial.print(centerX);
Serial.print(", Center Y: ");
Serial.println(centerY);
}
void loop() {
unsigned long currentTime = millis();
// Only send updates at the specified interval
if (currentTime - lastUpdate >= UPDATE_INTERVAL) {
lastUpdate = currentTime;
// Read joystick positions
int rawX = analogRead(VRX_PIN);
int rawY = analogRead(VRY_PIN);
// Calculate offset from center
int offsetX = rawX - centerX;
int offsetY = rawY - centerY;
// Apply deadzone
if (abs(offsetX) < DEADZONE) {
offsetX = 0;
}
if (abs(offsetY) < DEADZONE) {
offsetY = 0;
}
// Calculate movement deltas with sensitivity
int dx = (int)(offsetX * SENSITIVITY);
int dy = (int)(offsetY * SENSITIVITY);
// Only send if there's movement
if (dx != 0 || dy != 0) {
Serial.print(dx);
Serial.print(",");
Serial.println(dy);
}
// Optional: Handle button press (e.g., reset position or change color)
if (digitalRead(SW_PIN) == LOW) {
Serial.println("# Button pressed");
delay(200); // Simple debounce
}
}
}
/*
* WIRING INSTRUCTIONS:
* ====================
* Joystick Module -> Arduino
* ---------------------
* VCC -> 5V
* GND -> GND
* VRx -> A0
* VRy -> A1
* SW -> D2 (optional, for button)
*
* CALIBRATION:
* ============
* The code automatically calibrates the center position on startup.
* Keep the joystick centered (not touched) when powering on.
*
* ADJUSTMENTS:
* ============
* - Increase SENSITIVITY for faster cursor movement
* - Decrease SENSITIVITY for slower, more precise movement
* - Adjust DEADZONE if cursor drifts when joystick is centered
* - Change UPDATE_INTERVAL for faster/slower response
*
* TROUBLESHOOTING:
* ================
* 1. If movement is inverted, swap the formulas for dx/dy
* 2. If movement is too sensitive, decrease SENSITIVITY
* 3. If joystick drifts, increase DEADZONE
* 4. Check serial monitor at 115200 baud to see output
*/