Hello dear Forum
We are two mechanical engineering students, who are working on a 3D-Printer Project. As programing isn't our strenght, we would be very thankfull for some inputs. Our problem is currently the connection between our .C program and the Arduino Due.
The program is built to send Arduino a 'd' and receive a 'R' from Arduino, so it knows that he is ready to receive data. At some point in the sendport() it fails and repeats itself over and over trying to receive a R...
See .C programm for better understanding:
#include <stdio.h>
#include <windows.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <math.h>
#include <direct.h> // for _getcwd
#define ARDUINOPORT "COM7" // Change to your COM port
#define FALSE 0
#define TRUE 1
#define READ_TIMEOUT_MS 2000
#define PI 3.1415927
char FileName[1000] = ""; //stores the BMP file name
HANDLE hSerial; // Serial handle for Windows
int plotterSteps = 0; // stores the plotter steps for the picture
char PicturePath[1000]; // stores the directory path for the picture
//+++++++++++++++++++++++ Start clearSerialBuffer ++++++++++++++++++++++++++
// Clears the serial buffer by reading all available data
void clearSerialBuffer() {
char buff;
DWORD bytesRead;
while (ReadFile(hSerial, &buff, 1, &bytesRead, NULL) && bytesRead > 0) {
printf("Clearing buffer: '%c' (%d)\n", buff, buff);
}
if (bytesRead == 0) {
printf("Buffer cleared.\n");
} else {
printf("Failed to clear buffer.\n");
}
}
//-----------------------nd clearSerialBuffer ----------------------------------
//+++++++++++++++++++++++ Start readport ++++++++++++++++++++++++++
// Checks serial port for incoming Data
char readport(void) { // reads a single byte from the serial port and returns it if data is available
DWORD bytesRead; // store number of bytes read by ReadFile()
char buff; // store bytes read from the serial port (temporary)
LARGE_INTEGER start, now, frequency;
// Initialize high-resolution timing
QueryPerformanceFrequency(&frequency);
QueryPerformanceCounter(&start);
// Continuously attempt to read from the serial port until 'R' is received or timeout
while (1) {
if (ReadFile(hSerial, &buff, 1, &bytesRead, NULL)) {
if (bytesRead > 0) {
if (buff == 'R'|| buff == 'X') {
printf("readport received: '%c' (%d)\n", buff, buff);
return buff;
} else if (buff == '\r' || buff == '\n' || buff == 0) {
// Ignore unwanted characters
printf("readport ignored: '%c' (%d)\n", buff, buff);
continue;
} else {
printf("readport received unexpected character: '%c' (%d)\n", buff, buff);
}
}
}
return 0; // Return 0 if no data is available
// Check if the elapsed time has exceeded the timeout
QueryPerformanceCounter(&now);
double elapsedTime = (double)(now.QuadPart - start.QuadPart) * 1000.0 / frequency.QuadPart;
if (elapsedTime >= READ_TIMEOUT_MS) {
printf("readport timouted while waiting for 'R' response.\n");
return 0; // Return 0 to indicate a timeout
}
}
}
//------------------------ End readport ----------------------------------
//+++++++++++++++++++++++ Start sendport ++++++++++++++++++++++++++
// Sends a single byte of data and waits for Arduinos acknowledgment (R)
void sendport(unsigned char ValueToSend) {
printf("sendport called with ValueToSend: '%c' (%d)\n", ValueToSend, ValueToSend);
if (ValueToSend == 0) {
printf("sendport ignored null command.\n");
return;
}
DWORD bytesWritten; // Store number of bytes written by WriteFile()
LARGE_INTEGER startTime, currentTime, frequency;
QueryPerformanceFrequency(&frequency); // Get the high-resolution timer frequency
QueryPerformanceCounter(&startTime); // Get the starting time
if (!WriteFile(hSerial, &ValueToSend, 1, &bytesWritten, NULL)) {
printf("write() of value failed!\n");
return;
}
Sleep(100);
int receivedR = 0; // Flag to track if 'R' was received
while (1) {
char response = readport(); // Read a byte from the serial port
if (response == 'R') { // Check if the response is 'R'
QueryPerformanceCounter(¤tTime); // Get the current time
double responseTime = (double)(currentTime.QuadPart - startTime.QuadPart) * 1000.0 / frequency.QuadPart;
printf("Response time for command '%c': %.2f ms\n", ValueToSend, responseTime);
receivedR = 1; // Set the flag indicating 'R' was received
printf("receivedR is now: %d (expected 1)\n", receivedR); // Print the value of receivedR
break; // Exit the loop when 'R' is received
}
// Clear the serial buffer if the response is not 'R'
if (receivedR == 1) {
printf("Response received. Proceeding with next command...\n");
} else {
clearSerialBuffer(); // Only clear if necessary
}
// Check for timeout
QueryPerformanceCounter(¤tTime);
double elapsedTime = (double)(currentTime.QuadPart - startTime.QuadPart) / frequency.QuadPart;
if (elapsedTime > 2.0) { // Timeout set to 2 seconds
printf("Failed to receive 'R' response within timeout for command '%c'.\n", ValueToSend);
break; // Exit the loop on timeout
}
}
// If the loop exited because of timeout, ensure no residual looping
if (receivedR > 1) {
return;
printf("Exiting function due to timeout.\n");
printf("Final value of receivedR: %d\n", receivedR);
}
}
//------------------------ End sendport ----------------------------------
//+++++++++++++++++++++++ Start openport ++++++++++++++++++++++++++
// - opens the serial port (COM7)
// - configures the serial ports communication settings
// - sets timeout parameters for read and write operations
// - If any step fails (such as opening the port, getting or setting attributes, or setting timeouts), it prints an error message and exits.
void openport(void) {
hSerial = CreateFile( // used to open a handle to serial port COM7
ARDUINOPORT, // which port to open
GENERIC_READ | GENERIC_WRITE, // specifies the serial port for reading and writing
0, // No sharing with other processes
NULL, // Default security setting for the port
OPEN_EXISTING, // open port if it exists
0, // file attributes (none)
NULL); // no template file is used
if (hSerial == INVALID_HANDLE_VALUE) { // if port could not be opened, CreateFile returns INVALID_HANDLE_VALUE and exits over return
DWORD error = GetLastError();
printf("Unable to open port. Error code: %lu\n", error);
return;
}
DCB dcbSerialParams = { 0 }; // Device Control Block (contains settings of serial port)
dcbSerialParams.DCBlength = sizeof(dcbSerialParams);
if (!GetCommState(hSerial, &dcbSerialParams)) { // retrieves currents settings of serial port and stores them in dcbSerialParams
printf("Couldn't get serial port attributes\n"); // If it fails this will be printed and exits over return
return;
}
// setting serial port parameters
dcbSerialParams.BaudRate = CBR_256000; // sets baud rate to 115200 bits per second
dcbSerialParams.ByteSize = 8; // sets number of data bits per byte to 8
dcbSerialParams.StopBits = ONESTOPBIT; // 1 stop bit will be used to indicate the end of each byte
dcbSerialParams.Parity = NOPARITY; // no parity checking
if (!SetCommState(hSerial, &dcbSerialParams)) { // applies the serial port settings (stored in dcbSerialParams) to the open port
printf("Couldn't set serial port attributes\n");
return; // if it fails it will exit over return
}
// set timeouts for read and write operations for serial communication
COMMTIMEOUTS timeouts = { 0 };
timeouts.ReadIntervalTimeout = 100; // specifies the maximum time (ms) between the arrival of two consecutive bytes
timeouts.ReadTotalTimeoutConstant = 100; // a constant timeout for read operations
timeouts.ReadTotalTimeoutMultiplier = 10; // a multiplier for read timeouts. this influences the total timeout based on the amount of data being read
timeouts.WriteTotalTimeoutConstant = 100; // a constant timeout for write operations
timeouts.WriteTotalTimeoutMultiplier = 10; // same multiplier as read timeout
SetCommTimeouts(hSerial, &timeouts);
}
//------------------------ End openport ----------------------------------
//######################################################################
//################## Main ##############################################
//######################################################################
int main(int argc, char **argv) {
char FullFileName[1000] = ""; // A character array initialized to an empty string, intended to store the full file path of the BMP image file (up to 1000 characters).
FILE *bmpFile; // A file pointer used to open and access the BMP file in binary mode.
char FileInfo[3]; // A character array to store the BMP file identifier (typically "BM" for BMP files) and a null terminator (marks the end of a string).
long FileSize; // stores the size of the BMP file in bytes
long LongTemp; // A temporary variable used when reading unused sections of the BMP file header.
long DataOffset; // stores the offset (in bytes) from the beginning of the file to the start of the pixel data.
long HeaderSize; // stores the size of the BMP file header
long PictureWidth; // Stores the width of the image in pixels.
long PictureHeight; // Stores the height of the image in pixels.
int IntTemp; // A temporary variable used when reading additional fields of the BMP header.
int ColorDepth; // Stores the number of bits per pixel (should be 24 for a 24-bit BMP file)
long CompressionType; // Stores the compression type; it should be 0 for uncompressed BMP images.
long PictureSize; // Stores the size of the pixel data in bytes
long XPixelPerMeter; // Stores the horizontal and vertical resolution of the image (pixels per meter)
long YPixelPerMeter;
long ColorNumber; // Stores the number of colors in the color palette (used mainly for BMP files with color palettes)
long ColorUsed; // Stores the number of important colors; this is typically zero in 24-bit BMP files
unsigned char bitmapData[24][10000]; // a 2D array to store pixel data from the BMP file. It is organized to hold up to 24 rows, each with a width of up to 10,000 pixels
openport(); // opens serial port to arduino
printf("\n\nWaiting for 'X' from Arduino (Arduino plugged in?)...\n");
// Wait for 'X' from Arduino
char handshakeChar;
do {
handshakeChar = readport();
if (handshakeChar == 'X') {
printf("Connection to Arduino established.\n");
break;
}
printf("Unexpected character during handshake: '%c' (%d)\n", handshakeChar, handshakeChar);
} while (1);
// Wait for 'X' from Arduino
// while (readport() != 'X');
// printf("Connection to Arduino established.\n");
strcpy(FileName, "donut doppelt gross.bmp"); // Image file the program intends to process
_getcwd(PicturePath, 1000); // gets current working directory and stores it in PicturePath
strcat(PicturePath, "\\pictures"); // subdirectory where the image file is stored
printf("PicturePath=>%s<\n", PicturePath); // prints the full path to the PicturePath directory
if ((bmpFile = fopen(FileName, "rb")) == NULL) { // attempts to open image file in binary read mode (rb)
printf("File '%s' cannot be opened for reading!\n", FileName);
return 1;
}
fread(&FileInfo, 2, 1, bmpFile); // reads the first 2 bytes of the BMP file into FileInfo
fread(&FileSize, 4, 1, bmpFile); // Reads the next 4 bytes, which store the file size in bytes.
fread(&LongTemp, 4, 1, bmpFile); // Reads 4 bytes into LongTemp. This data is reserved and not often used for standard BMPs.
fread(&DataOffset, 4, 1, bmpFile); // Reads 4 bytes indicating where the actual pixel data begins.
fread(&HeaderSize, 4, 1, bmpFile); // Reads 4 bytes representing the size of the header structure.
fread(&PictureWidth, 4, 1, bmpFile); // These read 4 bytes each, storing the width and height of the image in pixels.
fread(&PictureHeight, 4, 1, bmpFile); // These read 4 bytes each, storing the width and height of the image in pixels
fread(&IntTemp, 2, 1, bmpFile); // Reads 2 bytes for the number of color planes (typically 1 for BMP)
fread(&ColorDepth, 2, 1, bmpFile); // Reads 2 bytes representing the bits per pixel (e.g., 24 for 24-bit color).
fread(&CompressionType, 4, 1, bmpFile); // Reads 4 bytes that indicate compression type (0 = uncompressed, which is standard for BMP).
fread(&PictureSize, 4, 1, bmpFile); // Reads 4 bytes storing the size of the image data (in bytes), not including headers.
fread(&XPixelPerMeter, 4, 1, bmpFile); // These read 4 bytes each, representing horizontal and vertical resolution in pixels per meter.
fread(&YPixelPerMeter, 4, 1, bmpFile); // These read 4 bytes each, representing horizontal and vertical resolution in pixels per meter.
fread(&ColorNumber, 4, 1, bmpFile); // Reads 4 bytes for the number of colors in the palette (if used).
fread(&ColorUsed, 4, 1, bmpFile); // Reads 4 bytes for the number of important colors. If zero, all colors are considered important.
// Prints all the fread
printf("FileInfo=%s\n", FileInfo);
printf("FileSize=%ld\n", FileSize);
printf("LongTemp=%ld\n", LongTemp);
printf("DataOffset=%ld\n", DataOffset);
printf("HeaderSize=%ld\n", HeaderSize);
printf("PictureWidth=%ld\n", PictureWidth);
printf("PictureHeight=%ld\n", PictureHeight);
printf("IntTemp=%d\n", IntTemp);
printf("ColorDepth=%d\n", ColorDepth);
printf("CompressionType=%ld\n", CompressionType);
printf("PictureSize=%ld\n", PictureSize);
printf("XPixelPerMeter=%ld\n", XPixelPerMeter);
printf("YPixelPerMeter=%ld\n", YPixelPerMeter);
printf("ColorNumber=%ld\n", ColorNumber);
printf("ColorUsed=%ld\n", ColorUsed);
// prints errors
if (FileInfo[0] != 'B' || FileInfo[1] != 'M') {
printf("Wrong Fileinfo (BM)!\n");
return 1;
}
if (ColorDepth != 24) {
printf("Wrong ColorDepth (24)!\n");
return 1;
}
if (CompressionType != 0) {
printf("Wrong CompressionType (0)!\n");
return 1;
}
// This adjustment (also known as "row padding") is essential for reading BMP image data correctly.
// Without aligning each row to a 4-byte boundary, the data might not appear correctly when processed, leading to misaligned or distorted images.
fseek(bmpFile, DataOffset, SEEK_SET);
long PictureWidthFilled = PictureWidth * 3;
while (PictureWidthFilled % 4 != 0) {
PictureWidthFilled++;
}
printf("PictureWidthFilled = %ld, PictureWidth = %ld\n", PictureWidthFilled, PictureWidth);
while (!feof(bmpFile)) {
// Reset buffer to 255 (White)
for (int i = 0; i < 24; i++) {
for (int j = 0; j < 10000; j++) {
bitmapData[i][j] = 255;
}
}
// Read 24 lines into buffer
for (long j = 0; j < 24; j++) {
if (!feof(bmpFile)) {
fread(bitmapData[j], 1, PictureWidthFilled, bmpFile);
}
printf("%d\n", 1 << j);
}
// Processing data to send...
for (long k = 0; k < 2; k++) {
for (long i = 0; i < PictureWidth * 3; i += 3) {
unsigned char dataToSend1 = 0;
unsigned char dataToSend2 = 0;
for (long j = 0; j < 8; j++) {
if (bitmapData[j * 2 + k][i] < 127 && bitmapData[j * 2 + k][i + 1] < 127 && bitmapData[j * 2 + k][i + 2] < 127) { // Black dot
dataToSend1 += 1 << j;
}
}
for (long j = 8; j < 12; j++) {
if (bitmapData[j * 2 + k][i] < 127 && bitmapData[j * 2 + k][i + 1] < 127 && bitmapData[j * 2 + k][i + 2] < 127) { // Black dot
dataToSend2 += 1 << (j - 8);
}
}
clearSerialBuffer();
sendport('d');
sendport(dataToSend1);
sendport(dataToSend2);
}
if (k == 0) {
sendport('r'); // Return carriage
sendport('N'); // Forward paper one dot
} else {
sendport('r'); // Return carriage
sendport('n'); // New line
}
}
}
fclose(bmpFile);
CloseHandle(hSerial); // Close the serial port
return 0;
}
We first thought our Arduino is too fast for our programm, so we checkt our baud rates and also implemented some waiting points in our Arduino IDE. But nothing worked on this side. What can we do, so our program is running step by step and not skipping any of the commands.
Here you have our Arduino programm:
#define LED_1 13
#define SWITCH_01 A0
#define NOZZLE_01 2
#define NOZZLE_02 3
#define NOZZLE_03 4
#define NOZZLE_04 5
#define NOZZLE_05 6
#define NOZZLE_06 7
#define NOZZLE_07 8
#define NOZZLE_08 9
#define NOZZLE_09 10
#define NOZZLE_10 11
#define NOZZLE_11 12
#define NOZZLE_12 A1
#define DIR_X A2
#define STEP_X A3
#define DIR_Y A4
#define STEP_Y A5
#define STEP_PAUSE 1000
#define COMMAND_PAUSE 5
#define DOT_PAUSE_LONG 1000
#define DOT_PAUSE_SHORT 5
int i;
char comByte = 0;
char dataMode = 0;
long dotWidth = 11;
long lineHeight = 234;
long stepsDone = 0;
long yStepsDone = 0;
int nozzles[] = {NOZZLE_06, NOZZLE_07, NOZZLE_08, NOZZLE_09, NOZZLE_12, NOZZLE_11, NOZZLE_10, NOZZLE_03, NOZZLE_02, NOZZLE_01, NOZZLE_04, NOZZLE_05};
// Initialize communication through USB
//void establishContact() {
//while (Serial.available() <= 0) {
//Serial.print('X'); // send a capital X
//delay(1000);
//}
//}
void motorStep(long stepWidth, int dirPin, int stepPin, long stepPause){
if(stepWidth < 0){
digitalWrite(dirPin, HIGH);
stepWidth = -stepWidth;
}
else{
digitalWrite(dirPin, LOW);
}
for(long i=0; i<stepWidth; i++){
digitalWrite(stepPin, HIGH);
delayMicroseconds(COMMAND_PAUSE);
digitalWrite(stepPin, LOW);
delayMicroseconds(stepPause);
}
}
void makeDot(int nozzle, long dotPause){
int i = 0;
digitalWrite(nozzle, HIGH);
delayMicroseconds(1);
//for(i=0; i<1; i++);
digitalWrite(nozzle, LOW);
delayMicroseconds(dotPause);
}
// the setup routine runs once when you press reset:
void setup(){
dataMode = 0;
stepsDone = 0;
yStepsDone = 0;
pinMode(LED_1, OUTPUT);
pinMode(SWITCH_01, INPUT);
digitalWrite(SWITCH_01, HIGH); // Activate pull-up resistor
pinMode(DIR_X, OUTPUT);
pinMode(STEP_X, OUTPUT);
pinMode(DIR_Y, OUTPUT);
pinMode(STEP_Y, OUTPUT);
for(i=0; i<12; i++){
pinMode(nozzles[i], OUTPUT);
}
motorStep(100, DIR_X, STEP_X, STEP_PAUSE);
motorStep(-100, DIR_X, STEP_X, STEP_PAUSE);
motorStep(100, DIR_Y, STEP_Y, STEP_PAUSE);
motorStep(-100, DIR_Y, STEP_Y, STEP_PAUSE);
// start serial port at 115200 bps:
Serial.begin(256000);
// Clear any lingering data
while (Serial.available()) {
Serial.read();
}
//establishContact();
Serial.print('X');
}
// the loop routine runs over and over again forever:
void loop() {
comByte = 0;
if (Serial.available() > 0) {//if valid, read from serial:
//get incoming byte:
char comByte = Serial.read();
// Serial.print("Received command: ");
// Serial.print(comByte);
if (comByte == 'd') {
// Perform actions specific to 'd' command here
// Send acknowledgment 'R' for 'd'
// Serial.println("Preparing to send 'R'");
while (Serial.available()) {
Serial.read(); // Clear lingering data
}
delay(10);
Serial.print('R'); // Send 'R' to signal PC
delay(1000);
}
// Serial.print('R'); // send 'R' to initiate next data from computer
if(dataMode == 0){
if(comByte == 'd'){
dataMode = 1;
}
if(comByte == 'r'){// Return line
motorStep(-stepsDone, DIR_X, STEP_X, STEP_PAUSE/10);
stepsDone = 0;
}
if(comByte == 'n'){// New line
motorStep(-lineHeight, DIR_Y, STEP_Y, STEP_PAUSE/10);
//motorStep(-dotWidth, DIR_X, STEP_X, STEP_PAUSE/10);//only why printhead wasn't othogonal
yStepsDone+=lineHeight;
stepsDone = 0;
}
if(comByte == 'N'){// Forward paper for one dot
motorStep(-lineHeight/12, DIR_Y, STEP_Y, STEP_PAUSE/10);
yStepsDone+=lineHeight/12;
stepsDone = 0;
}
if(comByte == 'G') {// Command to return to initial Y position
motorStep(yStepsDone, DIR_Y, STEP_Y, STEP_PAUSE/10); // Move back to the start
}
if(comByte == 'T'){// Test nozzles
for(int i = 0; i < 12; i++){
makeDot(nozzles[i], DOT_PAUSE_LONG);
delay(2000);
}
}
if(comByte == 't'){// Test nozzles
for(int i = 0; i < 12; i++){
for(int j = 0; j < 100; j++){
makeDot(nozzles[i], DOT_PAUSE_LONG);
motorStep(dotWidth, DIR_X, STEP_X, DOT_PAUSE_LONG/dotWidth + 10);
}
}
motorStep(-lineHeight, DIR_Y, STEP_Y, STEP_PAUSE/4);
for(int i = 0; i < 12; i++){
for(int j = 0; j < 100; j++){
makeDot(nozzles[i], DOT_PAUSE_LONG);
motorStep(-dotWidth, DIR_X, STEP_X, DOT_PAUSE_LONG/dotWidth + 10);
}
}
}
if(comByte == 'x'){
motorStep(100, DIR_X, STEP_X, STEP_PAUSE);
}
if(comByte == 'X'){
motorStep(-100, DIR_X, STEP_X, STEP_PAUSE);
}
if(comByte == 'y'){
motorStep(100, DIR_Y, STEP_Y, STEP_PAUSE);
}
if(comByte == 'Y'){
motorStep(-100, DIR_Y, STEP_Y, STEP_PAUSE);
}
}
else{
if(dataMode == 1){
for(int i = 0; i < 8; i++){
if(comByte & (1<<i)){
makeDot(nozzles[i], DOT_PAUSE_SHORT);
}
}
}
if(dataMode == 2){
for(int i = 0; i < 4; i++){
if(comByte & (1<<i)){
makeDot(nozzles[i+8], DOT_PAUSE_SHORT);
}
}
}
dataMode++;
if(dataMode == 3){
motorStep(dotWidth, DIR_X, STEP_X, DOT_PAUSE_LONG/dotWidth + 10);
stepsDone+=dotWidth;
dataMode = 0;
}
}
}//if (Serial.available() > 0){
}
And here is what our command prompt gives us:
We are kinda lost and would really appreciate your help:)
Thank you