Help with MEGA2560 + DM542 + C30 + UART Conflict Issues in Linear Stepper Motor Control Project

Hello Arduino Community,

I'm currently working on a project similar to a stamping machine, and I’ve encountered several issues I'd like your help with. Here's a summary of my setup and the problems I’m facing:


:toolbox: Hardware Configuration:

  • Linear Stepper Motor: SGP1650-200
  • Stepper Driver: DM542
  • Manual Stepper Speed Controller: C30 CNC Stepper Motor Direction/Speed Controller
  • Touch Screen: SEENGREAT 3.5-inch TFT LCD SPI Touch Screen
  • Microcontrollers:
    • Arduino UNO (dedicated to the LCD)
    • Arduino MEGA 2560 ADK R3 (controls the stepper motor and communicates with UNO)
  • Infrared Distance Sensor: TOF250
  • Power Supply: 12V, 29A, 350W Switching Power Supply

:electric_plug: System Architecture:

  1. The UNO is connected to the TFT LCD and handles the GUI interface.
  2. The MEGA receives commands via UART from the UNO.
  3. TOF250 IR sensor is connected to MEGA via Serial2 (TX2/RX2).
  4. The MEGA controls the linear stepper motor via DM542 driver.
  5. The C30 controller is also connected to the motor for manual operation.

:red_exclamation_mark:Problems I'm Facing:

  1. UNO ↔ MEGA UART conflicts — Sometimes the devices stop communicating or get stuck.
  2. DM542 ↔ MEGA issues — Even though the control code is uploaded, the motor doesn’t rotate.
  3. MEGA ↔ C30 control conflict — When both are connected, neither seems to work properly.
  4. I want manual (C30) and automatic (MEGA) control to coexist without interfering with each other.

:white_check_mark: What I'm Trying to Achieve:

  1. AUTO Mode (triggered from UNO LCD) — The motor moves down until TOF250 measures 10cm.
  2. CLEAR Mode — The motor moves up until 15cm.
  3. STOP Mode — Stops any ongoing motion immediately, overriding all other commands.
  4. C30 and MEGA should not interfere with each other (i.e., both connected, but not controlling at the same time).

I will attach the GUI code for UNO and the control code for MEGA if needed.
Can anyone help me identify what might be going wrong and how I can make both manual and automatic control work seamlessly?

Thanks in advance! :folded_hands:

#include <SPI.h>
#include <Wire.h>
#include <SoftwareSerial.h>

#include "lcd_3inch5.h"
#include "lcd_gui.h"
#include "lcd_touch.h"
#include "fonts.h"

SoftwareSerial megaSerial(2, 3);

struct Rect { uint16_t x1, y1, x2, y2; };
Rect bandAuto, bandStop, bandClear;

bool wasPressed = false;

bool inRect(uint16_t x, uint16_t y, const Rect& r) {
  return (x >= r.x1 && x <= r.x2 && y >= r.y1 && y <= r.y2);
}

void drawBand(const Rect& r, const char* label) {
  Gui_draw_rectangle(r.x1, r.y1, r.x2, r.y2, BLACK, 2, EMPTY);
  if (label && *label) {
    uint16_t tx = r.x1 + (r.x2 - r.x1 - Font24.w * strlen(label)) / 2;
    uint16_t ty = r.y1 + (r.y2 - r.y1 - Font24.h) / 2;
    Gui_draw_str(tx, ty, label, &Font24, BLACK, WHITE);
  }
}

void layoutInit() {
  const uint16_t totalMargin = 40;
  const uint16_t gapCount = 2;
  const uint16_t GAP_V = 15;
  uint16_t availableHeight = LCD_H - totalMargin - (gapCount * GAP_V);
  uint16_t BOX_H = availableHeight / 3;

  uint16_t x1 = 40;
  uint16_t x2 = LCD_W - 40;
  uint16_t y = totalMargin / 2;

  bandAuto  = {x1, y, x2, (uint16_t)(y + BOX_H)};
  y += BOX_H + GAP_V;
  bandStop  = {x1, y, x2, (uint16_t)(y + BOX_H)};
  y += BOX_H + GAP_V;
  bandClear = {x1, y, x2, (uint16_t)(y + BOX_H)};
}

void drawUI() {
  LCD_Clear(WHITE);
  drawBand(bandAuto, "Auto");
  drawBand(bandStop, "Stop");
  drawBand(bandClear, "Clear");
}

void showCenterText(const char* msg) {
  uint16_t textW = Font24.w * strlen(msg);
  uint16_t tx = (LCD_W - textW) / 2;
  uint16_t ty = (LCD_H - Font24.h) / 2;
  Gui_fill_color(0, ty - 5, LCD_W, ty + Font24.h + 5, WHITE);
  if (msg && *msg) {
    Gui_draw_str(tx, ty, msg, &Font24, BLACK, WHITE);
  }
}

void sendCommand(const char* cmd) {
  megaSerial.write(cmd);
}

void setup() {
  Serial.begin(9600);
  megaSerial.begin(9600);

  Lcd_Gpio_Init();
  SPI.begin();
  Lcd_Init();
  TP_Init();

  layoutInit();
  drawUI();
}

void loop() {
  uint8_t pressed = TP_Scan(0);

  if (pressed && !wasPressed) {
    uint16_t tx = tp_dev.x[0];
    uint16_t ty = tp_dev.y[0];

    if (inRect(tx, ty, bandAuto)) {
      Serial.println("AUTO");
      showCenterText("Moving...");
      sendCommand("A");
    }
    else if (inRect(tx, ty, bandStop)) {
      Serial.println("STOP");
      showCenterText("STOP!");
      sendCommand("S");
    }
    else if (inRect(tx, ty, bandClear)) {
      Serial.println("CLEAR");
      showCenterText("Clear...");
      sendCommand("C");
    }
  }
  else if (!pressed && wasPressed) {
    showCenterText("");
    drawBand(bandStop, "Stop");
  }

  wasPressed = pressed;
}

This is ARDUINO UNO's GUI code

#define STEP_PIN   9
#define EN_PIN    10

#define STEP_DELAY 700

volatile char currentCommand = '\0';

String distanceStr = "";
int currentDistance = -1;

const int DOWN_TARGET = 10;
const int UP_TARGET   = 15;

void setup() {
  Serial.begin(9600);
  Serial1.begin(9600);
  Serial2.begin(9600);

  pinMode(DIR_PIN, OUTPUT);
  pinMode(STEP_PIN, OUTPUT);
  pinMode(EN_PIN, OUTPUT);

  digitalWrite(EN_PIN, LOW);
  Serial.println("MEGA started");
}

void loop() {
  readDistanceSensor();
  readUnoCommand();

  if (currentCommand == 'A') {
    moveToTarget(DOWN_TARGET, true);
  } else if (currentCommand == 'C') {
    moveToTarget(UP_TARGET, false);
  } else {
    delay(10);
  }
}

void readUnoCommand() {
  if (Serial1.available()) {
    char cmd = Serial1.read();
    if (cmd == 'A' || cmd == 'C' || cmd == 'S') {
      currentCommand = cmd;
      Serial.print("Received command: ");
      Serial.println(cmd);
    }
  }
}

void readDistanceSensor() {
  while (Serial2.available()) {
    char c = Serial2.read();
    if (c == '\n') {
      currentDistance = distanceStr.toInt();
      distanceStr = "";
      Serial.print("Distance: ");
      Serial.print(currentDistance);
      Serial.println(" cm");
    } else if (isDigit(c)) {
      distanceStr += c;
    }
  }
}

void moveToTarget(int target, bool goingDown) {
  Serial.println(goingDown ? "Moving Down" : "Moving Up");

  digitalWrite(DIR_PIN, goingDown ? HIGH : LOW);

  while (true) {
    if (currentCommand == 'S') {
      Serial.println("STOP command received → Stopping movement");
      break;
    }

    if (currentDistance > 0) {
      if ((goingDown && currentDistance <= target) ||
          (!goingDown && currentDistance >= target)) {
        Serial.println("Target distance reached → Stopping");
        break;
      }
    }

    digitalWrite(STEP_PIN, HIGH);
    delayMicroseconds(STEP_DELAY);
    digitalWrite(STEP_PIN, LOW);
    delayMicroseconds(STEP_DELAY);
  }

  currentCommand = '\0';
}

I really want to solve this problem plz sombody help me...
and if you want my e-mail or instagram, I can give you.
i can give you pictures that you want

I am curious: why is a separate UNO required to drive the display?

In your first code snippet I don't see where its writing to 'megaSerial'? Is that maybe in the sendCommand() function? Could you show all of the serial processing code please?

In your second piece of code:

So what happens if currentCommand is 'A' or 'C'?
Is it always guaranteed that currentDistance > 0 ?
Otherwise, what happens if the enclosed conditions are not met?
In which case, are we sure that we can always exit the while (true) loop?

The

while (true) {}

loop will run forever so when using it either:

  • be absolutely sure that the running code can always break out of it under whatever conditions it is designed to process
  • or use it only when when the intention is to stay locked into it - as in to halt execution

Can you confirm that your stepper motor is rotating when these commands are executed ?
and i don't see this defined anywhere

pinMode(DIR_PIN, OUTPUT);

as a general rule it is best to test your sketch in parts before merging them together.
Once you know you can move the stepper, you can move it through commands and with limitations.

Also i don't see why you need a separate UNO for the GUI. If you are using an SPI LCD-TP you will need to connect the SPI pins to the Mega SPI pins (which are not the same as on the UNO !)

Again, see if you can get that to work.

Chances are this is because of swSerial usage on the UNO, quite possibly in combination with the GUI device. swSerial can be a solution, but it can also cause issues and therefore be a problem.

The code you shared doesn't compile, but start over with a simple stepper code and get that working first.

That works ?!

Without telling us how you have connected it, it is hard to know what is going on or wrong. Please post a schematic

Does that mean that 1 should disable the other ? and which one should be the primary ?

How are you handling the conflict between the display and SoftwareSerial on pin 3?

Hmm, I didn't delve into the detail of that device, but there are variants and if distanceStr is not getting any characters or garbage characters, then distanceStr.toInt(); will return a zero value. The program would then get caught in that WHILE loop which might explain the program getting "stuck". OP, it would be helpful to know exactly what TOF250 product you have?

Totally agree with this! Test each piece of hardware, function or concept in code separately to make sure it is working as you would expect, and only then add the next function of feature and test that thoroughly as well. Then move on to the next etc. Don't just throw code together and hope for the best.....

Because if you do, and it doesn't, you won't know where to look !