Need help with text position (solved)

Hi everyone, I’m creating the GUI for a ground station for a rocket I’m building with Arduino, or rather, chatGPT did it for me since I have no experience with processing and don’t have the time to learn it, so I’ll start by saying I almost don’t know what’s in the code. I know for sure that Arduino and this GUI communicate via a protocol I created, very simple. The problem is that some of the text is out of place or misaligned with the boxes. Chat wasn’t able to fix the code for me, so I’m asking if you have any advice to help me or if you have the time and desire to do me a favor and fix it for me (I don’t mean to be rude, I just need to finish this GUI quickly). In any case, here’s the code:
// === BASIC SETTINGS ===
import processing.serial.; // Import serial library for Arduino communication
import controlP5.
; // Import ControlP5 library for GUI buttons and sliders

Serial myPort;
ControlP5 cp5;

String serialInput = “”;
HashMap<String, String> data = new HashMap<String, String>(); // Stores all telemetry data
HashMap<String, Integer> servoDefaults = new HashMap<String, Integer>(); // Stores default servo positions
HashMap<String, Integer> servoValues = new HashMap<String, Integer>(); // Stores real-time servo positions

String modes = {“IDLE”, “GROUND_TEST”, “FLIGHT”}; // Left panel modes
String rightModes = {“ALIGN”, “FIN_TEST”}; // Right panel modes
int currentMode = 0;
boolean wasMousePressed = false;

void setup() {
size(1280, 720); // Set canvas size
surface.setTitle(“Rocket Ground Station - Meteor V3”);

cp5 = new ControlP5(this);
printArray(Serial.list()); // Print available serial ports
myPort = new Serial(this, Serial.list()[0], 115200); // Connect to the first available serial port
myPort.bufferUntil(‘\n’); // Read serial input line-by-line

setupGUI(); // Create GUI elements
initServos(); // Initialize default servo values
}

void draw() {
background(30);
textAlign(LEFT, BASELINE); // Reset text alignment

// Draw different sections of the interface
drawTelemetry();
drawServoPanel();
drawServoDefaults();
drawStatePanel();
drawRocketState();

wasMousePressed = mousePressed; // Used for button clicks
}

// === INITIALIZATION ===
void setupGUI() {
// Add left side mode buttons
for (int i = 0; i < modes.length; i++) {
cp5.addButton(modes[i])
.setPosition(20, 40 + i * 60)
.setSize(100, 50)
.onClick(e → changeMode(e.getController().getName()));
}

// Add right side mode buttons
for (int i = 0; i < rightModes.length; i++) {
cp5.addButton(rightModes[i])
.setPosition(1150, 40 + i * 60)
.setSize(100, 50)
.onClick(e → changeMode(e.getController().getName()));
}

// Reset Arduino button
cp5.addButton(“RESET ARDUINO”)
.setPosition(550, 660)
.setSize(180, 40)
.onClick(e → myPort.write(“RESET\n”));
}

void initServos() {
// Initialize servo positions to 90°
for (int i = 1; i <= 4; i++) {
String key = “S” + i;
servoValues.put(key, 90);
servoDefaults.put(key, 90);
}
}

// === SERIAL COMMUNICATION ===
void serialEvent(Serial p) {
serialInput = p.readStringUntil(‘\n’);
if (serialInput != null) {
serialInput = serialInput.trim();
if (serialInput.length() == 0) return;
if (!serialInput.contains(“:”)) return;
parseData(serialInput); // Parse and store incoming data
}
}

void parseData(String line) {
// Split serial line into key:value pairs
String parts = split(line, " “);
for (String part : parts) {
if (part.contains(”:")) {
String kv = split(part, “:”);
if (kv.length == 2) {
data.put(kv[0], kv[1]); // Store in data map
if (kv[0].startsWith(“S”)) {
servoValues.put(kv[0], int(kv[1]));
}
}
}
}
}

// === MODE HANDLING ===
void changeMode(String mode) {
int code = -1;
// Map mode name to corresponding code
if (mode.equals(“IDLE”)) code = 0;
if (mode.equals(“ALIGN”)) code = 1;
if (mode.equals(“GROUND_TEST”)) code = 2;
if (mode.equals(“FLIGHT”)) code = 3;
if (mode.equals(“FIN_TEST”)) code = 4;

// Send mode to Arduino if valid
if (code != -1) {
currentMode = code;
myPort.write(code + “\n”);
println("Modalità cambiata a: " + mode);
}
}

// === TELEMETRY PANEL ===
void drawTelemetry() {
int x = 150, y = 20, w = 260, h = 140, spacing = 20;

// Draw four telemetry boxes
drawBox(“Accelerometers”, x, y, w, h, “AX”, “AY”, “AZ”);
drawBox(“Gyroscopes”, x + w + spacing, y, w, h, “GX”, “GY”, “GZ”);
drawBox(“PID Output”, x, y + h + spacing, w, h, “OUTX”, “OUTY”, “OUTZ”);
drawBox(“Angles”, x + w + spacing, y + h + spacing, w, h, “ANGX”, “ANGY”, “ANGZ”);
}

void drawBox(String title, int x, int y, int w, int h, String… keys) {
fill(50);
stroke(200);
rect(x, y, w, h, 10);
fill(255);
textSize(14);
text(title, x + 10, y + 25);

textSize(12);
for (int i = 0; i < keys.length; i++) {
String val = data.containsKey(keys[i]) ? data.get(keys[i]) : “—”;
text(keys[i] + ": " + val, x + 10, y + 45 + i * 20);
}
}

// === MAIN STATUS PANEL ===
void drawStatePanel() {
int x = 150, y = 340, w = 540, h = 100;

fill(50);
stroke(200);
rect(x, y, w, h, 10);
fill(255);
textSize(14);
text(“Rocket Status”, x + 10, y + 25);

// Show current flight mode
String modeNames = {“MODE_IDLE”, “MODE_ALIGN_SERVO”, “MODE_GROUND_TEST”, “MODE_FLIGHT”, “MODE_FIN_TEST”};
String modeVal = data.getOrDefault(“MODE”, “—”);
String modeText = modeVal.matches(“\d”) ? modeNames[int(modeVal)] : modeVal;

// Get other state values
String launched = data.getOrDefault(“LAUNCHED”, “—”);
String offx = data.getOrDefault(“OFFX”, “—”);
String offy = data.getOrDefault(“OFFY”, “—”);

fill(255);
text("MODE: " + modeText, x + 10, y + 50);
text("OFFX: " + offx, x + 160, y + 50);
text("OFFY: " + offy, x + 310, y + 50);

// Color LAUNCHED status
if (launched.equals(“1”)) fill(0, 255, 0);
else fill(255, 0, 0);
text("LAUNCHED: " + launched, x + 10, y + 75);
}

// === SYSTEM STATE PANEL ===
void drawRocketState() {
int x = 710, y = 340, w = 400, h = 100;

fill(50);
stroke(200);
rect(x, y, w, h, 10);
fill(255);
textSize(14);
text(“Internal State”, x + 10, y + 25);

// This line calls drawStatusText(), which sometimes causes the error
drawStatusText(“CALIBRATING”, x + 10, y + 50);
drawStatusText(“OFFSETTING”, x + 210, y + 50);
drawStatusText(“MPU”, x + 10, y + 75);
}

// === This function is correctly declared! ===
// It displays a blinking or colored status based on value
void drawStatusText(String key, int x, int y) {
String val = data.getOrDefault(key, “—”);

if (val.equals(“1”)) {
if (frameCount % 30 < 15) fill(255, 0, 0);
else fill(255, 255, 0);
} else if (val.equals(“0”)) {
fill(0, 255, 0);
} else {
fill(200);
}

text(key + ": " + val, x, y);
}

// === SERVO STATUS PANEL ===
void drawServoPanel() {
int cx = 900, cy = 180;
fill(80);
stroke(200);
rect(cx - 120, cy - 120, 240, 240, 10);
fill(255);
textAlign(CENTER);
text(“Fin Status”, cx, cy - 130);
textAlign(LEFT);

String servos = {“S1”, “S2”, “S3”, “S4”};
int pos = {{cx, cy - 90}, {cx + 90, cy}, {cx, cy + 90}, {cx - 90, cy}};

for (int i = 0; i < servos.length; i++) {
String s = servos[i];
int x = pos[i][0];
int y = pos[i][1];

fill(servoValues.get(s) == null ? 150 : 255);
ellipse(x, y, 40, 40);
fill(0);
textAlign(CENTER, CENTER);
text(s + "\n" + servoValues.get(s), x, y);

}
textAlign(LEFT, BASELINE);
}

// === SERVO DEFAULT PANEL ===
void drawServoDefaults() {
int startX = 820, y = 500;

fill(50);
stroke(200);
rect(startX - 20, y - 40, 280, 180, 10);
fill(255);
textSize(14);
text(“Default Fin Positions”, startX, y - 10);

String servos = {“S1”, “S2”, “S3”, “S4”};
for (int i = 0; i < servos.length; i++) {
String s = servos[i];
int x = startX;
int sy = y + i * 30;
int val = servoDefaults.get(s);

text(s + ": " + val + "°", x, sy);

drawButton(x + 60, sy - 10, 30, 20, "-1", () -> changeDefault(s, -1));
drawButton(x + 100, sy - 10, 30, 20, "0", () -> resetDefault(s));
drawButton(x + 140, sy - 10, 30, 20, "+1", () -> changeDefault(s, +1));

}
}

// === GENERIC BUTTON HANDLER ===
void drawButton(int x, int y, int w, int h, String label, Runnable action) {
fill(100);
rect(x, y, w, h, 5);
fill(255);
textAlign(CENTER, CENTER);
text(label, x + w / 2, y + h / 2);

boolean inside = mouseX > x && mouseX < x + w && mouseY > y && mouseY < y + h;
if (mousePressed && !wasMousePressed && inside) {
action.run();
}
}

// === SERVO VALUE CHANGERS ===
void changeDefault(String servo, int delta) {
int current = servoDefaults.get(servo);
int newVal = constrain(current + delta, 70, 110);
servoDefaults.put(servo, newVal);
myPort.write(servo + “:” + newVal + “\n”);
println("Default command: " + servo + “:” + newVal);
}

void resetDefault(String servo) {
servoDefaults.put(servo, 90);
myPort.write(servo + “:90\n”);
println("Reset default: " + servo + “:90”);
}
P.S. Chat also wrote the code comments.

Thanks everyone in advance.

Hello @Meteor122 ,

Please read:

Above asks that you format your code and also links to this:
https://discourse.processing.org/faq#format-your-code

:)

I’m really sorry but I can’t format it in any way, neither with the button nor manually.

This topic is also posted here:

https://www.reddit.com/r/processing/comments/1m3ar8f/need_help_with_text_position

This part from your code


void drawBox(String title, int x, int y, int w, int h, String… keys) {
fill(50);
stroke(200);
rect(x, y, w, h, 10);
fill(255);
textSize(14);
text(title, x + 10, y + 25);

textSize(12);
for (int i = 0; i < keys.length; i++) {
String val = data.containsKey(keys[i]) ? data.get(keys[i]) : “—”;
text(keys[i] + ": " + val, x + 10, y + 45 + i * 20);
}
}

This line change text position change 10,25 values see what happens


text(title, x + 10, y + 25);

This line change the text result position of
void drawBox(String title, int x, int y, int w, int h, String… keys) {

Change 10 and 45 see what happens

text(keys[i] + ": " + val, x + 10, y + 45 + i * 20);

thx a lot, i fixed all the code and now is so beautiful. thanks dude

Yes, you can, here is how…

  1. In your original post, click the “edit” pencil.
  2. Select your code text.
  3. “cut” the code text (CTRL-X)
  4. Click the </> button
  5. … at this point, do not click anywhere or move the cursor…
  6. Paste (CTRL-V) the code text
  7. Click “Save Edit” to save the edit.

The result with your code will look like this… (updated ```cpp) (further updated ```processing)

// === BASIC SETTINGS ===
import processing.serial.; // Import serial library for Arduino communication
import controlP5.; // Import ControlP5 library for GUI buttons and sliders

Serial myPort;
ControlP5 cp5;

String serialInput = "";
HashMap<String, String> data = new HashMap<String, String>(); // Stores all telemetry data
HashMap<String, Integer> servoDefaults = new HashMap<String, Integer>(); // Stores default servo positions
HashMap<String, Integer> servoValues = new HashMap<String, Integer>(); // Stores real-time servo positions

String modes = {"IDLE", "GROUND_TEST", "FLIGHT"}; // Left panel modes
String rightModes = {"ALIGN", "FIN_TEST"}; // Right panel modes
int currentMode = 0;
boolean wasMousePressed = false;

void setup() {
  size(1280, 720); // Set canvas size
  surface.setTitle("Rocket Ground Station - Meteor V3");

  cp5 = new ControlP5(this);
  printArray(Serial.list()); // Print available serial ports
  myPort = new Serial(this, Serial.list()[0], 115200); // Connect to the first available serial port
  myPort.bufferUntil('\n'); // Read serial input line-by-line

  setupGUI(); // Create GUI elements
  initServos(); // Initialize default servo values
}

void draw() {
  background(30);
  textAlign(LEFT, BASELINE); // Reset text alignment

  // Draw different sections of the interface
  drawTelemetry();
  drawServoPanel();
  drawServoDefaults();
  drawStatePanel();
  drawRocketState();

  wasMousePressed = mousePressed; // Used for button clicks
}

// === INITIALIZATION ===
void setupGUI() {
  // Add left side mode buttons
  for (int i = 0; i < modes.length; i++) {
    cp5.addButton(modes[i])
      .setPosition(20, 40 + i * 60)
      .setSize(100, 50)
      .onClick(e → changeMode(e.getController().getName()));
  }

  // Add right side mode buttons
  for (int i = 0; i < rightModes.length; i++) {
    cp5.addButton(rightModes[i])
      .setPosition(1150, 40 + i * 60)
      .setSize(100, 50)
      .onClick(e → changeMode(e.getController().getName()));
  }

  // Reset Arduino button
  cp5.addButton("RESET ARDUINO")
    .setPosition(550, 660)
    .setSize(180, 40)
    .onClick(e → myPort.write("RESET\n"));
}

void initServos() {
  // Initialize servo positions to 90°
  for (int i = 1; i <= 4; i++) {
    String key = "S" + i;
    servoValues.put(key, 90);
    servoDefaults.put(key, 90);
  }
}

// === SERIAL COMMUNICATION ===
void serialEvent(Serial p) {
  serialInput = p.readStringUntil('\n');
  if (serialInput != null) {
    serialInput = serialInput.trim();
    if (serialInput.length() == 0) return;
    if (!serialInput.contains(":")) return;
    parseData(serialInput); // Parse and store incoming data
  }
}

void parseData(String line) {
  // Split serial line into key:value pairs
  String parts = split(line, " “);
  for (String part : parts) {
    if (part.contains(":")) {
      String kv = split(part, ":");
      if (kv.length == 2) {
        data.put(kv[0], kv[1]); // Store in data map
        if (kv[0].startsWith("S")) {
          servoValues.put(kv[0], int(kv[1]));
        }
      }
    }
  }
}

// === MODE HANDLING ===
void changeMode(String mode) {
  int code = -1;
  // Map mode name to corresponding code
  if (mode.equals("IDLE")) code = 0;
  if (mode.equals("ALIGN")) code = 1;
  if (mode.equals("GROUND_TEST")) code = 2;
  if (mode.equals("FLIGHT")) code = 3;
  if (mode.equals("FIN_TEST")) code = 4;

  // Send mode to Arduino if valid
  if (code != -1) {
    currentMode = code;
    myPort.write(code + "\n");
    println("Modalità cambiata a: " + mode);
  }
}

// === TELEMETRY PANEL ===
void drawTelemetry() {
  int x = 150, y = 20, w = 260, h = 140, spacing = 20;

  // Draw four telemetry boxes
  drawBox("Accelerometers", x, y, w, h, "AX", "AY", "AZ");
  drawBox("Gyroscopes", x + w + spacing, y, w, h, "GX", "GY", "GZ");
  drawBox("PID Output", x, y + h + spacing, w, h, "OUTX", "OUTY", "OUTZ");
  drawBox("Angles", x + w + spacing, y + h + spacing, w, h, "ANGX", "ANGY", "ANGZ");
}

void drawBox(String title, int x, int y, int w, int h, String… keys) {
  fill(50);
  stroke(200);
  rect(x, y, w, h, 10);
  fill(255);
  textSize(14);
  text(title, x + 10, y + 25);

  textSize(12);
  for (int i = 0; i < keys.length; i++) {
    String val = data.containsKey(keys[i]) ? data.get(keys[i]) : "—";
    text(keys[i] + ": " + val, x + 10, y + 45 + i * 20);
  }
}

// === MAIN STATUS PANEL ===
void drawStatePanel() {
  int x = 150, y = 340, w = 540, h = 100;

  fill(50);
  stroke(200);
  rect(x, y, w, h, 10);
  fill(255);
  textSize(14);
  text("Rocket Status", x + 10, y + 25);

  // Show current flight mode
  String modeNames = {"MODE_IDLE", "MODE_ALIGN_SERVO", "MODE_GROUND_TEST", "MODE_FLIGHT", "MODE_FIN_TEST"};
  String modeVal = data.getOrDefault("MODE", "—");
  String modeText = modeVal.matches("\d") ? modeNames[int(modeVal)] : modeVal;

  // Get other state values
  String launched = data.getOrDefault("LAUNCHED", "—");
  String offx = data.getOrDefault("OFFX", "—");
  String offy = data.getOrDefault("OFFY", "—");

  fill(255);
  text("MODE: " + modeText, x + 10, y + 50);
  text("OFFX: " + offx, x + 160, y + 50);
  text("OFFY: " + offy, x + 310, y + 50);

  // Color LAUNCHED status
  if (launched.equals("1")) fill(0, 255, 0);
  else fill(255, 0, 0);
  text("LAUNCHED: " + launched, x + 10, y + 75);
}

// === SYSTEM STATE PANEL ===
void drawRocketState() {
  int x = 710, y = 340, w = 400, h = 100;

  fill(50);
  stroke(200);
  rect(x, y, w, h, 10);
  fill(255);
  textSize(14);
  text("Internal State", x + 10, y + 25);

  // This line calls drawStatusText(), which sometimes causes the error
  drawStatusText("CALIBRATING", x + 10, y + 50);
  drawStatusText("OFFSETTING", x + 210, y + 50);
  drawStatusText("MPU", x + 10, y + 75);
}

// === This function is correctly declared! ===
// It displays a blinking or colored status based on value
void drawStatusText(String key, int x, int y) {
  String val = data.getOrDefault(key, "—");

  if (val.equals("1")) {
    if (frameCount % 30 < 15) fill(255, 0, 0);
    else fill(255, 255, 0);
  } else if (val.equals("0")) {
    fill(0, 255, 0);
  } else {
    fill(200);
  }

  text(key + ": " + val, x, y);
}

// === SERVO STATUS PANEL ===
void drawServoPanel() {
  int cx = 900, cy = 180;
  fill(80);
  stroke(200);
  rect(cx - 120, cy - 120, 240, 240, 10);
  fill(255);
  textAlign(CENTER);
  text("Fin Status", cx, cy - 130);
  textAlign(LEFT);

  String servos = {"S1", "S2", "S3", "S4"};
  int pos = {{cx, cy - 90}, {cx + 90, cy}, {cx, cy + 90}, {cx - 90, cy}};

  for (int i = 0; i < servos.length; i++) {
    String s = servos[i];
    int x = pos[i][0];
    int y = pos[i][1];

    fill(servoValues.get(s) == null ? 150 : 255);
    ellipse(x, y, 40, 40);
    fill(0);
    textAlign(CENTER, CENTER);
    text(s + "\n" + servoValues.get(s), x, y);
  }
  textAlign(LEFT, BASELINE);
}

// === SERVO DEFAULT PANEL ===
void drawServoDefaults() {
  int startX = 820, y = 500;

  fill(50);
  stroke(200);
  rect(startX - 20, y - 40, 280, 180, 10);
  fill(255);
  textSize(14);
  text("Default Fin Positions", startX, y - 10);

  String servos = {"S1", "S2", "S3", "S4"};
  for (int i = 0; i < servos.length; i++) {
    String s = servos[i];
    int x = startX;
    int sy = y + i * 30;
    int val = servoDefaults.get(s);

    text(s + ": " + val + "°", x, sy);

    drawButton(x + 60, sy - 10, 30, 20, "-1", () -> changeDefault(s, -1));
    drawButton(x + 100, sy - 10, 30, 20, "0", () -> resetDefault(s));
    drawButton(x + 140, sy - 10, 30, 20, "+1", () -> changeDefault(s, +1));
  }
}

// === GENERIC BUTTON HANDLER ===
void drawButton(int x, int y, int w, int h, String label, Runnable action) {
  fill(100);
  rect(x, y, w, h, 5);
  fill(255);
  textAlign(CENTER, CENTER);
  text(label, x + w / 2, y + h / 2);

  boolean inside = mouseX > x && mouseX < x + w && mouseY > y && mouseY < y + h;
  if (mousePressed && !wasMousePressed && inside) {
    action.run();
  }
}

// === SERVO VALUE CHANGERS ===
void changeDefault(String servo, int delta) {
  int current = servoDefaults.get(servo);
  int newVal = constrain(current + delta, 70, 110);
  servoDefaults.put(servo, newVal);
  myPort.write(servo + ":" + newVal + "\n");
  println("Default command: " + servo + ":" + newVal);
}

void resetDefault(String servo) {
  servoDefaults.put(servo, 90);
  myPort.write(servo + ":90\n");
  println("Reset default: " + servo + ":90");
}

I do not know why this code does not have highlighted keywords…

See the section on Syntax highlighting:

FAQ - Processing Community Forum < I learned this recently and I am an experienced forum member!

That is a start.
The code you posted still has errors.
We do not have the original source to work and can only work with what was pasted which may have additional errors lost in translation.

In a ideal world code should be formatted properly by the original poster, reviewed and tested before posting.

All users, and especially new users, should be required to read all the guidelines before posting.

These days I follow this approach to unformatted code:

The above reflects my opinions on this matter.
We are all trying to help!

:)

@glv - Referring to my post/code two posts ago… Thank you! I used cpp after the three backticks for the code I posted… giving it highlights… nice… learning every day.

I was addressing the formatting statement “I can’t format it in any way..” to give another method, assuming their first two applications (IDE and browser) were not cooperating.

Cool beans! I learned something new as well!

ChatGPT assisted research:

The most reliable list of language specifiers for Highlight.js, which is used by Discourse, is maintained on their official GitHub repository.

And processing is in there!

This is a cool list!

:)

Isn’t that for c plus plus? I would think you’d want to use processing for this forum.

1 Like

I have updated the code post to ```processing

2 Likes