USB Serial Communication between Teensy 4.0 and C++ app

Status
Not open for further replies.

ShantalW

Member
I posted earlier but realized my code was probably a bit long, so made a simpler piece of code, although it's still long... hard to set up serial communication in a short fashion, but hopefully short enough it might be bearable to read. My goal is to get a program (written in C++) to be able to talk to the teensy to give it commands and receive data from sensors via a USB connection (using the micro USB port). This sample program I have made is super simple and instead just turns one of two lights on or off or responds that the message wasn't valid. The important part is the talking though. Where I have the code now I can send a single command and have the teensy perform that command's action, or it can constantly read what the teensy is saying, but once something is attempted to be sent, it stops receiving messages, and doesn't even react to that sent message. I also can't get the teensy to get more than one message from the program on my PC. It works fine if I am not using a program but instead just use the Serial Monitor in Teensydruino. Any help would be greatly appreciated!

Teensy code (.ino):
Code:
int LED1Pin = 9;
int LED2Pin = 11;

String computerMessage = "";

void setup() {
  // put your setup code here, to run once:
pinMode(LED1Pin, OUTPUT);
pinMode(LED2Pin, OUTPUT);
}

void loop() {
  // put your main code here, to run repeatedly:
  if(Serial.available() != 0)
  {
    computerMessage = Serial.readStringUntil('\n');
  }
  if(computerMessage != "")
  {
    if(computerMessage == "1On")
    {
      digitalWrite(LED1Pin, HIGH);
      Serial.println("LED 1 is now on");
    }
    else if(computerMessage == "2On")
    {
      digitalWrite(LED2Pin, HIGH);
      Serial.println("LED 2 is now on");
    }
    else if(computerMessage == "1Off")
    {
      digitalWrite(LED1Pin, LOW);
      Serial.println("LED 1 is now off");
    }
    else if(computerMessage == "2Off")
    {
      digitalWrite(LED2Pin, LOW);
      Serial.println("LED 2 is now off");
    }
    else
    {
      Serial.println("Command Not Available");
    }
    
    computerMessage = "";
  }

}

PC Code (.cpp):
Code:
#define TEENSY_WAIT_TIME 200
#define MAX_DATA_LENGTH 255
#define SEND_DATA_LENGTH		255
#define RECEIVE_DATA_LENGTH		255
#define NL_TEENSY_PORT_NAME		"\\\\.\\COM"

#include <Windows.h>
#include <iostream>
#include <string>
#include <thread>

class TeensySerialPort
{
public:
	explicit TeensySerialPort(const char* portName);
	~TeensySerialPort();

	int readSerialPort(const char* buffer, unsigned int buf_size);
	bool writeSerialPort(const char* buffer, unsigned int buf_size);
	bool isConnected();
	void closeSerial();

private:
	HANDLE handler;
	bool connected;
	COMSTAT status;
	DWORD errors;
};

class TeensyMessageHandler
{
public:
	TeensyMessageHandler();
	~TeensyMessageHandler();

	static TeensyMessageHandler* GetInstance();
	static bool DestroyInstance();

	void Init();

	void UpdateTeensyMessageHandler();

	void SendMessageToTeensy(std::string szCommand);

private:

	static TeensyMessageHandler* m_pTeensyMessageHandler;

	std::string m_szTeensyMessage;

	std::string GetTeensyMessage();

	TeensySerialPort* Teensy = new TeensySerialPort("\\\\.\\COM7");

	char output[MAX_DATA_LENGTH];
	char incoming[MAX_DATA_LENGTH];

	std::thread* m_pHandleTeensyThread;
	void DoHandleTeensyThread();

	void CloseTeensyThread();

	std::string m_szTeensyPortName;
	bool m_bHandleTeensyThreadActive;
	bool m_bPauseNeeded;

};

TeensySerialPort::TeensySerialPort(const char* portName)
{
	this->connected = false;

	this->handler = CreateFileA(static_cast<LPCSTR>(portName),
		GENERIC_READ | GENERIC_WRITE,
		0,
		NULL,
		OPEN_EXISTING,
		FILE_ATTRIBUTE_NORMAL,
		NULL);

	if (this->handler == INVALID_HANDLE_VALUE)
	{
		if (GetLastError() == ERROR_FILE_NOT_FOUND)
		{
			std::cout << "Teensy Serial Port Error: Handle not attached. Port Unavailable. Port Name: " << portName << std::endl;
		}
		else
		{
			std::cout << "Teensy Serial Port Error.  Port Name: " << portName << std::endl;
		}

	}
	else
	{
		DCB dcbSerialParameters = { 0 };

		if (!GetCommState(this->handler, &dcbSerialParameters))
		{
			std::cout << "Failed to get current serial parameters. Port Name: " << portName << std::endl;
		}
		else
		{
			dcbSerialParameters.BaudRate = CBR_115200;
			dcbSerialParameters.ByteSize = 8;
			dcbSerialParameters.StopBits = ONESTOPBIT;
			dcbSerialParameters.Parity = NOPARITY;
			dcbSerialParameters.fDtrControl = DTR_CONTROL_ENABLE;

			if (!SetCommState(handler, &dcbSerialParameters))
			{
				std::cout << "Could not set serial port parameters.  Port Name: " << portName << std::endl;
			}
			else
			{
				this->connected = true;
				PurgeComm(this->handler, PURGE_RXCLEAR | PURGE_TXCLEAR);
				Sleep(TEENSY_WAIT_TIME);
			}
		}
	}
}

TeensySerialPort::~TeensySerialPort()
{
	if (this->connected)
	{
		this->connected = false;
		CloseHandle(this->handler);
	}
}

int TeensySerialPort::readSerialPort(const char* buffer, unsigned int buf_size)
{
	DWORD bytesRead{};
	unsigned int toRead = 0;

	ClearCommError(this->handler, &this->errors, &this->status);

	if (this->status.cbInQue > buf_size)
	{
		toRead = buf_size;
	}
	else
	{
		toRead = this->status.cbInQue;
	}

	memset((void*)buffer, 0, buf_size);

	if (ReadFile(this->handler, (void*)buffer, toRead, &bytesRead, NULL))
	{
		return bytesRead;
	}

	return 0;
}

// sending provided buffer to serial port;
// returns true if succeed, false if failed
bool TeensySerialPort::writeSerialPort(const char* buffer, unsigned int buf_size)
{
	DWORD bytesSend;

	if (!WriteFile(this->handler, (void*)buffer, buf_size, &bytesSend, 0))
	{
		ClearCommError(this->handler, &this->errors, &this->status);
		return false;
	}
	return true;
}

//checking if serial port is connected
bool TeensySerialPort::isConnected()
{
	if (this != nullptr)
	{
		if (!ClearCommError(this->handler, &this->errors, &this->status))
		{
			this->connected = false;
		}

		return this->connected;
	}
	else
	{
		return false;
	}
}

void TeensySerialPort::closeSerial()
{
	CloseHandle(this->handler);
}

TeensyMessageHandler* TeensyMessageHandler::m_pTeensyMessageHandler;

TeensyMessageHandler::TeensyMessageHandler() :
	m_szTeensyMessage(""),
	m_szTeensyPortName(NL_TEENSY_PORT_NAME + 7),
	incoming(""),
	m_bHandleTeensyThreadActive(false),
	m_bPauseNeeded(true)
{
}

TeensyMessageHandler::~TeensyMessageHandler()
{
	if (Teensy != nullptr)
	{
		Teensy->closeSerial();
	}
	CloseTeensyThread();
}

void TeensyMessageHandler::Init()
{
	m_pHandleTeensyThread = new std::thread(&TeensyMessageHandler::DoHandleTeensyThread, this);
}

void TeensyMessageHandler::DoHandleTeensyThread()
{
	m_bHandleTeensyThreadActive = true;

	int i = 6;
	while (!Teensy->isConnected())
	{
		std::string TeensyPortName = m_szTeensyPortName;
		std::cout << "Could Not Connect to Teensy.  Port: " << TeensyPortName << std::endl;
		delete Teensy;
		m_szTeensyPortName = NL_TEENSY_PORT_NAME + std::to_string(i);

		Teensy = new TeensySerialPort(m_szTeensyPortName.c_str());
		i++;
		if (i >= 20)
		{
			i = 1;
		}

	}

	if (m_bPauseNeeded)
	{
		Sleep(1000);
		m_bPauseNeeded = false;
	}

	std::string TeensyPortName = m_szTeensyPortName;
	std::cout << "Connected to Teensy.  Port: " << TeensyPortName << std::endl;

	char receivedMessage[RECEIVE_DATA_LENGTH];
	std::string	receivedMessageAsString = "";

	while (Teensy->isConnected())
	{
		if (Teensy != nullptr)
		{
			Teensy->readSerialPort(receivedMessage, RECEIVE_DATA_LENGTH);
			receivedMessageAsString = receivedMessage;
			if (receivedMessageAsString != "")
			{
				std::cout << receivedMessageAsString << std::endl;
			}

		}
	}
	m_bHandleTeensyThreadActive = false;
}

void TeensyMessageHandler::UpdateTeensyMessageHandler()
{
	if (m_bHandleTeensyThreadActive == false)
	{
		CloseTeensyThread();
		m_pHandleTeensyThread = new std::thread(&TeensyMessageHandler::DoHandleTeensyThread, this);
	}
}

void TeensyMessageHandler::CloseTeensyThread()
{
	if (m_pHandleTeensyThread != nullptr)
	{
		m_pHandleTeensyThread->join();
		delete m_pHandleTeensyThread;
		m_pHandleTeensyThread = nullptr;
	}
}

void TeensyMessageHandler::SendMessageToTeensy(std::string Command)
{
	const char* sendString = Command.c_str();

	if (Teensy->isConnected())
	{
		bool hasWritten = false;
		hasWritten = Teensy->writeSerialPort(sendString, SEND_DATA_LENGTH);

		if (hasWritten)
		{
			std::cout << "Sent Teensy Message: " << Command << std::endl;
		}
		else
		{
			std::cout << "Failed to send Teensy message: " << Command << std::endl;
		}
	}
}

std::string TeensyMessageHandler::GetTeensyMessage()
{
	char receivedString[RECEIVE_DATA_LENGTH];

	if (Teensy->isConnected())
	{
		int hasRead = Teensy->readSerialPort(receivedString, RECEIVE_DATA_LENGTH);
	}

	return std::string(receivedString);
}

TeensyMessageHandler* TeensyMessageHandler::GetInstance()
{
	if (!m_pTeensyMessageHandler)
	{
		m_pTeensyMessageHandler = new TeensyMessageHandler();
		m_pTeensyMessageHandler->Init();
	}
	return m_pTeensyMessageHandler;
}

bool TeensyMessageHandler::DestroyInstance()
{
	bool bSuccess = false;
	if (m_pTeensyMessageHandler)
	{
		delete m_pTeensyMessageHandler;
		bSuccess = true;
	}
	return bSuccess;
}

std::string UserCommand;

std::thread* pGetUserCommandsThread;

void CloseUserCommandsThread()
{
	if (pGetUserCommandsThread != nullptr)
	{
		pGetUserCommandsThread->join();
		delete pGetUserCommandsThread;
		pGetUserCommandsThread = nullptr;
	}
}

void GetUserCommands()
{
	TeensyMessageHandler* pTeensyMessageHandler = TeensyMessageHandler::GetInstance();
	while (true)
	{
		std::cin >> UserCommand;
		pTeensyMessageHandler->SendMessageToTeensy(UserCommand);
	}
}

int main()
{
	TeensyMessageHandler* pTeensyMessageHandler = TeensyMessageHandler::GetInstance();
	Sleep(1000);

	pGetUserCommandsThread = new std::thread(&GetUserCommands);

	while (true)
	{
		pTeensyMessageHandler->UpdateTeensyMessageHandler();

		Sleep(200);
	}

	CloseUserCommandsThread();

	return 0;
}
 
Status
Not open for further replies.
Back
Top