c++arduinoservo

Arduino servo code resetting to positon 0


(excuse my english) Hi. I'm a beginner at coding who started trying things in Arduino and c++ (because of Arduino). Recently I just learned that you could write to the Arduino board via the serial monitor, and thus via your PC through your COM port, so I tried to find a way of controlling a servo motors rotation from cmd via Arduino's serial monitor. I copied code that was given as an example in the Arduino website and via someone from youtube and got a piece of code that worked, sort of.

Because you see this code works as in it writes to the serial monitor via a c++ program on windows (any ide), but when I send a command, it runs correctly, then it runs once again but setting the input to 0 degrees thus moving the servo head to 0 degrees. I "fixed" it temporarily by preventing the servo from rotating if the input is 0 but it runs everything else in the sketch. (I've included the code below but it's really long and I don't exactly know how to include big pieces of code (apologies if I included stuff wrong) while still including the minimum reproducible example. I'll happily change it if told to)

The main code:

#include <iostream>
#include <string>
#include <stdlib.h>
#include <string>

#include "SerialPort.h"


char output[MAX_DATA_LENGTH];
char incomingData[MAX_DATA_LENGTH];

using namespace std;


char temp[] = "\\\\.\\COM4";
char* port = temp;

float Scalar = 0.8888;

int main() {
    SerialPort arduino(port);
    if (arduino.isConnected()) {
        cout << "Connection established" << endl << endl;
    }
    else {
        cout << "Error in port name" << endl << endl;
    }
    while (arduino.isConnected()) {
        while (true) {
            cout << "Enter your command (degrees 0 to 180 in integers): " << endl;
            string data;
            cin >> data;
            if (data == "exit")
                return 0;
            /*if (stoi(data) != 90) {
                data = to_string(
                    round((stof(data) - 90) * Scalar + 90)
                );
            }*/
            //cout << data << endl;

            char* charArray = new char[data.size() + 2];
            copy(data.begin(), data.end(), charArray);
            charArray[data.size()] = '\n';
            charArray[data.size() + 1] = '\0';
            cout << "char array: " << charArray << endl;

            arduino.writeSerialPort(charArray, MAX_DATA_LENGTH);
            arduino.readSerialPort(output, MAX_DATA_LENGTH);

            cout << ">> " << output << endl;
            cout << "----------\n" << endl;
            delete[] charArray;

        }
    }
    return 0;
}

The .ino sketch file:

#include <Servo.h>

Servo servo1;
int LED = 13;
int servoPos = 90; //to store servo's position

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  pinMode(LED, OUTPUT);
  servo1.attach(11);
  servo1.write(servoPos);
}

void loop() {
  // put your main code here, to run repeatedly:
  delay(15);
  if(Serial.available() > 0) {
    String info;
    String return_text;
    info = Serial.readStringUntil('\n');

    return_text = "info variable string is: " + info;
    Serial.println(return_text);
    
    if(info.toInt() < servoPos && info.toInt() != 0) {
      for( 0; servoPos > info.toInt(); servoPos-=1) {
        servo1.write(servoPos); 
        delay(5);
      }
    }
    else if(info.toInt() > servoPos) {
      for( 0; servoPos < info.toInt(); servoPos+=1) {
        servo1.write(servoPos);
        delay(5);
      }
    }
    digitalWrite(LED, HIGH);
    delay(20);
    digitalWrite(LED, LOW);
    
    //Serial.println("");
    //Serial.print("Servo position: ");
    //Serial.println(servoPos);
  }
}

The header:

#ifndef SERIALPORT_H
#define SERIALPORT_H

#define ARDUINO_WAIT_TIME 2000
#define MAX_DATA_LENGTH 255

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>

class SerialPort
{
private:
    HANDLE handler;
    bool connected;
    COMSTAT status;
    DWORD errors;
public:
    SerialPort(char* portName);
    ~SerialPort();

    int readSerialPort(char* buffer, unsigned int buf_size);
    bool writeSerialPort(char* buffer, unsigned int buf_size);
    bool isConnected();
};

#endif // SERIALPORT_H

And the cpp file containing the functions:

#include "SerialPort.h"

SerialPort::SerialPort(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) {
            printf("ERROR: Handle was not attached. Reason: %s not available\n", portName);
        }
        else
        {
            printf("ERROR!!!");
        }
    }
    else {
        DCB dcbSerialParameters = { 0 };

        if (!GetCommState(this->handler, &dcbSerialParameters)) {
            printf("failed to get current serial parameters");
        }
        else {
            dcbSerialParameters.BaudRate = CBR_9600;
            dcbSerialParameters.ByteSize = 8;
            dcbSerialParameters.StopBits = ONESTOPBIT;
            dcbSerialParameters.Parity = NOPARITY;
            dcbSerialParameters.fDtrControl = DTR_CONTROL_ENABLE;

            if (!SetCommState(handler, &dcbSerialParameters))
            {
                printf("ALERT: could not set Serial port parameters\n");
            }
            else {
                this->connected = true;
                PurgeComm(this->handler, PURGE_RXCLEAR | PURGE_TXCLEAR);
                Sleep(ARDUINO_WAIT_TIME);
            }
        }
    }
}

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

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

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

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

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

    return 0;
}

bool SerialPort::writeSerialPort(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;
    }
    else return true;
}

bool SerialPort::isConnected()
{
    return this->connected;
}

What happens: So when I run it, first the servo rotates to 90 degrees, then when I give it an integer input the Arduino runs the servo to the desired input, powers on and off the LED, and whilst this happens the cpp main code prints what I've sent to the Arduino (or at least the visible stuff without \n and \0 e.t.c) and what the serial monitor wrote and goes back to asking me to send an input. The problem is the Arduino code runs again and moves the servo to 0 degrees and lights on and off the LED, just like before without my request.

I went and prevented the servo from moving if data == 0, but the rest of the .ino sketch still runs, so I suspect it maybe have something to do with the char that the cpp file sends to the monitor..? But I'm too smooth brained to figure it out, thus asking for any advice. The console shows the output but doesn't show anything during the second time the arduino loop happens. If I write something in the console whilst the second (undesired) iteration happens then the arduino doesn't respond to my input, and it also messes up output from the serial monitor during the next runs by either not showing text or completely messing up the text and/or displaying weird gibberish like "³ô¦ò»]½R↨ùýº§»⌂2{²à_Ú◄/4ý¾ı" and more.

(If anything needs to be added, removed or changed because of either misconduct, etiquette or e.t.c then as I said I'll be more than happy to) thanks in advance.


Solution

  • Apologies for not updating. I found the solution. It was just that I asked the function from the code online to send a char array of 255 characters when I provided it with the length of whatever I sent. Since the function/header was tasked to send 255 characters it somehow made up random data which the arduino serial COM reader read as 0. I just fixed it by telling the function how much data to send by changing MAX_DATA_LENGTH in arduino.writeSerialPort(charArray, MAX_DATA_LENGTH); to just the number of characters in the string to send.