Code keeps compiling with errors

I'm a complete noob in c++ and do not understand the problems that keep occurring with this code

The code that I'm using is from GitHub - ngfelixl/FreeRC: RC Plane remotecontrol and boardcomputer based on Arduino using Visual Studio . I'm trying to compile the Mega file but it keeps giving me errors when trying to compile.

These are the errors
/var/folders/xm/qdqc6lfx63d9zx1srzd__9yw0000gp/T//ccsR5pEZ.ltrans0.ltrans.o: In function global constructors keyed to 65535_0_mega2560.ino.cpp.o.12387': <artificial>:(.text.startup+0xa6): undefined reference to Ds4::Ds4()'
:(.text.startup+0xbe): undefined reference to Screen::Screen(RF24*, unsigned char*, unsigned char*, unsigned char*)' /var/folders/xm/qdqc6lfx63d9zx1srzd__9yw0000gp/T//ccsR5pEZ.ltrans0.ltrans.o: In function setup':
/Users/User/Downloads/FreeRC-master-2/Mega/mega2560/mega2560.ino:48: undefined reference to Screen::init()' /Users/User/Downloads/FreeRC-master-2/Mega/mega2560/mega2560.ino:49: undefined reference to Ds4::init()'
/var/folders/xm/qdqc6lfx63d9zx1srzd__9yw0000gp/T//ccsR5pEZ.ltrans0.ltrans.o: In function loop': /Users/User/Downloads/FreeRC-master-2/Mega/mega2560/mega2560.ino:57: undefined reference to Ds4::get()'
/Users/User/Downloads/FreeRC-master-2/Mega/mega2560/mega2560.ino:64: undefined reference to Screen::update_analog_axis(unsigned char, unsigned char, unsigned char, bool)' /Users/User/Downloads/FreeRC-master-2/Mega/mega2560/mega2560.ino:65: undefined reference to Screen::update_analog_axis(unsigned char, unsigned char, unsigned char, bool)'
/Users/User/Downloads/FreeRC-master-2/Mega/mega2560/mega2560.ino:67: undefined reference to Screen::update_motor(unsigned char)' /Users/User/Downloads/FreeRC-master-2/Mega/mega2560/mega2560.ino:70: undefined reference to Screen::switch_view(String)'
/Users/User/Downloads/FreeRC-master-2/Mega/mega2560/mega2560.ino:74: undefined reference to Screen::navigate(bool, bool, bool, bool, bool, bool, bool, bool, bool)' /Users/User/Downloads/FreeRC-master-2/Mega/mega2560/mega2560.ino:130: undefined reference to Screen::update_battery(unsigned char, bool)'
/Users/User/Downloads/FreeRC-master-2/Mega/mega2560/mega2560.ino:131: undefined reference to Screen::update_voltage(float, bool)' /Users/User/Downloads/FreeRC-master-2/Mega/mega2560/mega2560.ino:132: undefined reference to Ds4::connected()'
/Users/User/Downloads/FreeRC-master-2/Mega/mega2560/mega2560.ino:135: undefined reference to Screen::print_peripheral_status(int, char*, char*, bool)' /Users/User/Downloads/FreeRC-master-2/Mega/mega2560/mega2560.ino:145: undefined reference to Screen::print_peripheral_status(int, char*, char*, bool)'
collect2: error: ld returned 1 exit status

exit status 1

Compilation error: exit status 1
Can someone help me successfully compile this?

Post the code, using code tags.

1 Like

This is my code but also to compile it it uses custom libraries

/*
 Name:		Sketch1.ino
 Created:	10.04.2017 12:00:00
 Author:	Felix Lemke

*  NRF24 Wiring (Arduino AtMeta 2560)
*  CE 40, CSN 53, MOSI - 50, MISO - 51
*  SCK 52, GND GND, VCC 3.3V
*
*  USB Host Shield plugged in
*/

#include <SPI.h>
#include "RF24.h"
#include "printf.h"
#include <PS4USB.h>
#include <usbhub.h>
#include "screen/Screen.h"
#include "ds4/Ds4.h"

#define CE_pin  53
#define CSN_pin 49

// Declare and initiate radio data
uint64_t writingPipe = 0xF0F0F0F0AA;
uint64_t readingPipe = 0xF0F0F0F0BB;
RF24 radio(CE_pin, CSN_pin);
unsigned long radioTransmission = 0, readUsb = 0, counter_check_status = 0;


uint16_t transmission_success = 0;
uint16_t transmission_count = 0;
uint8_t transmission_quality = 0;

char* navigation_status = "";
uint8_t voltage = 0, voltage_count = 0;
float voltage_sum = 0, voltage_avg = 0;

uint8_t channel_map[4] = { 0, 0, 0, 0 }; // Values in range 0-7
uint8_t axis_range_min[4] = { 30, 20, 20, 20 }; // Values in range 0-7
uint8_t axis_range_max[4] = { 160, 160, 160, 160 }; // Values in range 0-7
uint8_t transmission[5] = { 0, 0, 0, 0, 0 }; // Moter, Channel 1-4

Ds4 controller;
Screen screen(&radio, channel_map, axis_range_min, axis_range_max);

void setup() {
	screen.init();
	controller.init();
	setupRadio(); // Setup NRF24L01+
}


void loop() {
	if (millis() - readUsb > 20) {
		readUsb = millis();
		controller.get();
		if (screen.view == "control") {
			for (uint8_t i = 0; i < 4; i++) {
				uint8_t axis_id = channel_map[i] / 2;
				if(axis_id >= 0 && axis_id <= 3)
					transmission[i + 1] = map(controller.axis[axis_id] * ((channel_map[i]+1)%2 - channel_map[i]%2) + channel_map[i]%2 * 255, 0, 255, axis_range_min[i], axis_range_max[i]);
			}
			screen.update_analog_axis(0, controller.axis[0], controller.axis[1], false);
			screen.update_analog_axis(1, controller.axis[2], controller.axis[3], false);

			screen.update_motor(controller.axis[4]);
			if (controller.button.options) {
				controller.status = "options";
				screen.switch_view("options");
			}
		}
		else if (screen.view == "options" || screen.view == "channels" || screen.view == "range" || screen.view == "set_range") {
			navigation_status = screen.navigate(controller.button.left, controller.button.right, controller.button.up, controller.button.down, controller.button.x, controller.button.circle, controller.button.options, controller.button.l1, controller.button.r1);
		}
	}


	if (millis() - radioTransmission > 4) {
		// Write the radioData struct to the NRF24L01 module to send the data
		// and print success or error message
		radioTransmission = millis();
		uint8_t axis = map(controller.axis[0], 0, 255, 20, 160);

		radio.writeFast(&transmission, sizeof(transmission));

		if (radio.isAckPayloadAvailable()) {
			radio.read(&voltage, sizeof(voltage));
			voltage_sum = voltage_sum + voltage/10.0;
			voltage_count++;

			if (voltage_count == 20) {
				voltage_avg = voltage_sum / 20.0;
				voltage_sum = 0.0;
				voltage_count = 0;
			}
		}

		if (radio.txStandBy()) {
			transmission_success++;
		}


		//radio.setChannel(2);
		//radio.setChannel(3);
		//radio.setChannel(4);


		transmission_count++;
		if (transmission_count >= 100 || transmission_success >= 100) {
			transmission_quality = 10 * transmission_success / 100.0;
			transmission_count = 0;
			transmission_success = 0;
		}
	}


	if (millis() - counter_check_status > 500 && screen.view == "control") {
		// Check if a force update is required. This is the case when
		// returning to control view

		// Draw the plane object with the acceleration values (acceleration not implemented yet)
		// screen.draw_plane((controller.axis[0] - 127) /32.0, (controller.axis[1] - 127) / 32.0, -9);

		bool force = false;
		if (navigation_status == "back to control")
			force = true;

		// Update the battery level and the controller status
		screen.update_battery(controller.battery, force);
		screen.update_voltage(voltage_avg, force);
		if (controller.connected())
			screen.print_peripheral_status(2, "success", "Connected", force);
		else
			screen.print_peripheral_status(2, "danger", "Error", force);
		
		// Update the transmission quality
		if (transmission_quality > 8)
			screen.print_peripheral_status(0, "success", "Very good", force);
		else if (transmission_quality > 5)
			screen.print_peripheral_status(0, "warning", "Ok", force);
		else if (transmission_quality > 0)
			screen.print_peripheral_status(0, "danger", "Bad", force);
		else
			screen.print_peripheral_status(0, "danger", "No Signal", force);


		

		// Reset Force message
		if (navigation_status == "back to control")
			navigation_status = "";

		counter_check_status = millis();
	}
}

void setupRadio() {
	radio.begin();

	// Use PALevel low for testing purposes only (default: high)
	// PALevel now adjustable via options menu, default: high
	radio.setPALevel(RF24_PA_HIGH);
	radio.setDataRate(RF24_250KBPS);
	radio.setPayloadSize(sizeof(transmission));
	radio.setChannel(80);

	// Open a writing and reading pipe on each radio, with opposite addresses
	radio.openWritingPipe(writingPipe);
	radio.enableAckPayload();
	//radio.enableDynamicAck();
	//radio.openReadingPipe(1, readingPipe);
}

Try reading the errors. They are telling you that the code is expecting to find files and functions that do not exist.
For example the first error is:-
undefined reference to Ds4::Ds4()'
So it is looking in the folder
/var/folders/xm/qdqc6lfx63d9zx1srzd__9yw0000gp/T//ccsR5pEZ.ltrans0.ltrans.o:
And in that place it is looking in the function:-
global constructors keyed to 65535_0_mega2560.ino.cpp.o.12387'
and is finding a reference to :-
Ds4::Ds4() and finding nothing.

This means there is something wrong with the

#include "ds4/Ds4.h"

Library.

So you will have to post them before anyone can check what it is actually doing.

The compiler does not seem to be compiling the libraries correctly, possibly because of the difference in how the Arduino IDE and Visual Studio operate. The IDE does not appear to be compiling the .cpp file automatically, might be related to the files being in a subdirectory of the sketch folder. If you create a directory named "src" in the sketch folder, then move the ds4 and screen directories there, the libraries compile properly after making a few changes. The .h files do not implement compilation guards properly, the #endif statement needs to be after the body of the .h file, as it is only the #define statement is prevented from compiling more than once. The file names also need to be consistent, it may not matter on windows, but on other operation systems Ds4.h and ds4.cpp being upper/lower case can be a problem. The same applies to the screen library.

< edit > subdirectory should be named "src", not "scr" as I originally posted (it has been corrected above).

These are the custom libraries
ds4.cpp

#include"Ds4.h"

Ds4::Ds4() {}

void Ds4::init() {
	USB usb;
	PS4USB ps4 = PS4USB(&usb);
	usb_setup();
}

void Ds4::get() {
	usb.Task();
	// Check if DS4 is correctly recognized and get DS4 Data
	
	if (ps4.connected()) {
		axis[0] = ps4.getAnalogHat(LeftHatX);
		axis[1] = ps4.getAnalogHat(LeftHatY);
		axis[2] = ps4.getAnalogHat(RightHatX);
		axis[3] = ps4.getAnalogHat(RightHatY);
		axis[4] = ps4.getAnalogButton(L2);
		axis[5] = ps4.getAnalogButton(R2);

		button.options = ps4.getButtonClick(OPTIONS);
		button.up = ps4.getButtonClick(UP);
		button.down = ps4.getButtonClick(DOWN);
		button.left = ps4.getButtonClick(LEFT);
		button.right = ps4.getButtonClick(RIGHT);
		button.x = ps4.getButtonClick(X);
		button.circle = ps4.getButtonClick(CIRCLE);
		button.square = ps4.getButtonClick(SQUARE);
		button.triangle = ps4.getButtonClick(TRIANGLE);
		button.r1 = ps4.getButtonClick(R1);
		button.l1 = ps4.getButtonClick(L1);

		battery = ps4.getBatteryLevel();
	}
	else {
		usb_setup();
	}
}

void Ds4::usb_setup() {
	if (usb.Init() == -1) {
	}
	else {
	}
}

bool Ds4::connected() {
	bool flag = false;
	usb.Task();
	if (ps4.connected())
		flag = true;
	return flag;
}

Ds4.h

#ifndef DS4_H
#define DS4_H
#endif

#include <PS4USB.h>
#include <usbhub.h>


class Ds4 {
typedef struct button {
	bool x, circle, square, triangle, up, down, left, right, options, r1, l1;
};

private:
	USB usb;
	PS4USB ps4 = PS4USB(&usb);

	uint8_t middle = 127;
	uint8_t deadzone = 50;
	void usb_setup();

public:
	char *status;
	uint8_t axis[6];
	button button;
	uint8_t battery;
	uint8_t motor;

	Ds4();
	void init();
	void get();
	void controller(); 
        bool connected();
};

screen.cpp

#include"Screen.h"


Screen::Screen(RF24 *radio, uint8_t *channel_map, uint8_t *axis_range_min, uint8_t *axis_range_max) {
	tft = new Adafruit_TFTLCD(LCD_CS, LCD_CD, LCD_WR, LCD_RD, LCD_RESET);
	this->radio = radio;
	this->channel_map = channel_map;
	menu = Menu(tft, radio);
	menu_channels = Menu(tft, channel_map);
	menu_range = Menu(tft, axis_range_min, axis_range_max, &view);
}

void Screen::init() {
	tft->reset();
	tft->begin(0x9341);
	tft->setRotation(3);
	delay(50);
	initial_view();
}

bool Screen::update() {
	if (millis() - update_counter > SCREENUPDATE) {
		update_counter = millis();
		return true;
	}
	return false;
}

char* Screen::navigate(bool left, bool right, bool up, bool down, bool x, bool circle, bool options, bool l1, bool r1) {
	char *action = "";
	char *output = "";
	if (view == "options") {
		action = menu.execute(left, right, x, circle);


		if (action == "exit") {
			switch_view("control");
			output = "back to control";
		}

		if (action == "Channels") {
			switch_view("channels");
		}
		else if (action == "Axis Range") {
			switch_view("range");
		}


		if (up) {
			menu.previous();
		}
		else if (down) {
			menu.next();
		}
		if (options || circle) {
			switch_view("control");
			output = "back to control";
		}
	}
	else if (view == "channels") {
		action = menu_channels.execute(left, right, x, circle);

		if (up) {
			menu_channels.previous();
		}
		else if (down) {
			menu_channels.next();
		}

		if (options) {
			switch_view("control");
			output = "back to control";
		}

		if (circle || action == "exit") {
			switch_view("options");
		}
	}
	else if (view == "range") {
		action = menu_range.execute(left, right, x, circle);
		if (up) {
			menu_range.previous();
		}
		else if (down) {
			menu_range.next();
		}
		if (circle || action == "exit") {
			switch_view("options");
		}
	}
	else if (view == "set_range") {
		action = menu_range.execute(left, right, x, circle);
		if (left || right) {
			menu_range.range_toggle();
		}
		else if (r1) {
			menu_range.range_set(+10);
		}
		else if (l1) {
			menu_range.range_set(-10);
		}
		else if (up) {
			menu_range.range_set(+1);
		}
		else if (down) {
			menu_range.range_set(-1);
		}
		if (circle) {
			switch_view("range");
		}
	}
	

	return output;
}

void Screen::print_peripheral_status(int id, char *type, char *message, bool force) {
	char *status = "";
	bool status_updated=false; 

	if (id == 0)
		status = transmitter_status;
	else if (id == 2)
		status = controller_status;


	if (status != message || force) {
		if (type == "success") tft->setTextColor(GREEN);
		else if (type == "warning") tft->setTextColor(YELLOW);
		else if (type == "danger") tft->setTextColor(RED);

		tft->setCursor(150, 40 + 10 * id);
		tft->fillRect(150, 40 + 10 * id, 100, 10, BLACK);
		tft->println(message);
		status_updated = true;
		delay(3);
	}

	if (status_updated) {
		if (id == 0)
			transmitter_status = message;
		else if (id == 2)
			controller_status = message;
	}
}


void Screen::update_analog_axis(uint8_t axis, uint8_t x, uint8_t y, bool force) {

	uint8_t width = 48;
	uint8_t height = 48;

	if (axis == 0) { // Left Stick
		if (x != left_axis_pos[0] || y != left_axis_pos[1] || force) {
			tft->drawLine(11 + left_axis_pos[0] / 255.0*width, 177 + left_axis_pos[1] / 255.0*height, 15 + left_axis_pos[0] / 255.0*width, 177 + left_axis_pos[1] / 255.0 * height, BLACK);
			tft->drawLine(13 + left_axis_pos[0] / 255.0*width, 175 + left_axis_pos[1] / 255.0*height, 13 + left_axis_pos[0] / 255.0*width, 179 + left_axis_pos[1] / 255.0 * height, BLACK);

			tft->drawLine(11 + 24, 177 + 24, 15 + 24, 177 + 24, TEALBLUE);
			tft->drawLine(13 + 24, 175 + 24, 13 + 24, 179 + 24, TEALBLUE);

			tft->drawLine(11 + x / 255.0*width, 177 + y / 255.0*height, 15 + x / 255.0*width, 177 + y / 255.0 * height, ORANGE);
			tft->drawLine(13 + x / 255.0*width, 175 + y / 255.0*height, 13 + x / 255.0*width, 179 + y / 255.0 * height, ORANGE);

			left_axis_pos[0] = x;
			left_axis_pos[1] = y;
		}
	}else if (axis == 1) { // Left Stick
		if (x != right_axis_pos[0] || y != right_axis_pos[1] || force) {
			tft->drawLine(255 + right_axis_pos[0] / 255.0*width, 177 + right_axis_pos[1] / 255.0*height, 259 + right_axis_pos[0] / 255.0*width, 177 + right_axis_pos[1] / 255.0 * height, BLACK);
			tft->drawLine(257 + right_axis_pos[0] / 255.0*width, 175 + right_axis_pos[1] / 255.0*height, 257 + right_axis_pos[0] / 255.0*width, 179 + right_axis_pos[1] / 255.0 * height, BLACK);

			tft->drawLine(255 + 24, 177 + 24, 259 + 24, 177 + 24, TEALBLUE);
			tft->drawLine(257 + 24, 175 + 24, 257 + 24, 179 + 24, TEALBLUE);

			tft->drawLine(255 + x / 255.0*width, 177 + y / 255.0*height, 259 + x / 255.0*width, 177 + y / 255.0 * height, ORANGE);
			tft->drawLine(257 + x / 255.0*width, 175 + y / 255.0*height, 257 + x / 255.0*width, 179 + y / 255.0 * height, ORANGE);

			right_axis_pos[0] = x;
			right_axis_pos[1] = y;
		}
	}
}

void Screen::update_motor(uint8_t motor) {
	//if (motor != motor_state) {
		tft->fillRect(11, 101, 4, 62 - motor / 255.0 * 62, DARKGRAY);
		tft->fillRect(11, 101 + 62 - motor / 255.0 * 62, 4, motor / 255.0 * 62, ORANGE);
		motor_state = motor;
	//}
}

void Screen::update_battery(uint8_t ds4_battery, bool force) {
	if (ds4_battery_state != ds4_battery || force) {
		tft->fillRect(303, 11, 6, 18-ds4_battery/15.0*18, DARKGRAY);
		tft->fillRect(303, 11+18-18*ds4_battery/15.0, 6, ds4_battery/15.0*18, GREEN);
		ds4_battery_state = ds4_battery;
	}
	delay(5);
}

void Screen::switch_view(String change_to) {
	view = change_to;
	if (view == "control") initial_view();
	else if (view == "options") menu.display("");
	else if (view == "channels") menu_channels.display("");
	else if (view == "range") menu_range.display("");
}

void Screen::update_voltage(float voltage, bool force) {
	if (battery_state != voltage || force) {
		if (voltage < 10) {
			tft->setTextColor(ORANGE);
			tft->setCursor(278, 40);
		}
		else {
			tft->setTextColor(WHITE);
			tft->setCursor(272, 40);
		}
		tft->fillRect(272, 40, 30, 10, BLACK);
		tft->println(voltage);
		battery_state = voltage;
	}
	delay(3);
}

void Screen::draw_plane(double x, double y, double z) {
	// Draws black lines over the old orange lines
	// Calculates the new rotation matrix and the position of the new lines
	// in x and z plane. Afterwards print the new lines on the tft

	if (Rot[0][0] != NULL) {
		for (uint8_t i = 0; i < 12; i++) {
			line[0] = origin[0] + Rot[0][0] * plane_vec[i][0] + Rot[0][1] * plane_vec[i][1] + Rot[0][2] * plane_vec[i][2];
			line[2] = origin[0] + Rot[0][0] * plane_vec[i][3] + Rot[0][1] * plane_vec[i][4] + Rot[0][2] * plane_vec[i][5];

			line[1] = origin[0] + Rot[2][0] * plane_vec[i][0] + Rot[2][1] * plane_vec[i][1] + Rot[2][2] * plane_vec[i][2];
			line[3] = origin[0] + Rot[2][0] * plane_vec[i][3] + Rot[2][1] * plane_vec[i][4] + Rot[2][2] * plane_vec[i][5];
			tft->drawLine(line[0], line[1], line[2], line[3], BLACK);
		}
	}

	double d = sqrt(x * x + y * y + z * z);
	double norm[3] = { x / d, y / d, z / d };
	alpha = acos(norm[0]) + PI/2;
	phi = acos(norm[2]);
	double n[3] = { cos(alpha), sin(alpha), 0 };
	Rot[0][0] = n[0] * n[0] * (1 - cos(phi)) + cos(phi);
	Rot[0][1] = n[0] * n[1] * (1 - cos(phi)) - n[2] * sin(phi);
	Rot[0][2] = n[0] * n[2] * (1 - cos(phi)) + n[1] * sin(phi);

	Rot[1][0] = n[0] * n[1] * (1 - cos(phi)) + n[2] * sin(phi);
	Rot[1][1] = n[1] * n[1] * (1 - cos(phi)) + cos(phi);
	Rot[1][2] = n[1] * n[2] * (1 - cos(phi)) - n[0] * sin(phi);

	Rot[2][0] = n[0] * n[2] * (1 - cos(phi)) - n[1] * sin(phi);
	Rot[2][1] = n[1] * n[2] * (1 - cos(phi)) + n[0] * sin(phi);
	Rot[2][2] = n[2] * n[2] * (1 - cos(phi)) + cos(phi);

	for (uint8_t i = 0; i < 12; i++) {
		line[0] = origin[0] + Rot[0][0] * plane_vec[i][0] + Rot[0][1] * plane_vec[i][1] + Rot[0][2] * plane_vec[i][2];
		line[2] = origin[0] + Rot[0][0] * plane_vec[i][3] + Rot[0][1] * plane_vec[i][4] + Rot[0][2] * plane_vec[i][5];

		line[1] = origin[0] + Rot[2][0] * plane_vec[i][0] + Rot[2][1] * plane_vec[i][1] + Rot[2][2] * plane_vec[i][2];
		line[3] = origin[0] + Rot[2][0] * plane_vec[i][3] + Rot[2][1] * plane_vec[i][4] + Rot[2][2] * plane_vec[i][5];
		tft->drawLine(line[0], line[1], line[2], line[3], ORANGE);
	}
}




void Screen::initial_view() {
	tft->fillScreen(BLACK);
	tft->setCursor(10, 10);
	tft->setTextColor(ORANGE);
	tft->setTextSize(2);
	tft->println("RC Flight Control");
	tft->setTextColor(WHITE);
	tft->setTextSize(1);

	// Add peripherals entry under title
	tft->setCursor(10, 40);
	tft->println("NRF24L01+");
	tft->setCursor(10, 50);
	tft->println("USB Host Shield");
	tft->setCursor(10, 60);
	tft->println("PS4 Controller");

	// Add subsection titles
	tft->setTextColor(ORANGE);
	tft->setCursor(10, 75);
	tft->println("Device Feedback");
	tft->drawLine(10, 85, 242, 85, ORANGE);
	tft->setCursor(260, 75);
	tft->println("DS4");
	tft->drawLine(260, 85, 310, 85, ORANGE);
	tft->setTextColor(WHITE);

	// Draw Motor Status box
	tft->setCursor(10, 90);
	tft->println("Motor");
	tft->drawRect(10, 100, 6, 64, WHITE);

	// Draw Left Axis Box
	tft->drawRect(10, 174, 56, 56, WHITE);

	// Draw Right Axis Box
	tft->drawRect(254, 174, 56, 56, WHITE);

	// Draw Battery Level Boxes
	tft->drawRect(302, 10, 8, 20, WHITE);
	tft->drawRect(304, 8, 4, 3, WHITE);

	// Draw Accelerometer box
	tft->drawRect(76, 100, 168, 130, WHITE);


	draw_plane(-4, 0, -9);


	tft->setCursor(304, 40);
	tft->println("V");
}

Screen.h

#ifndef SCREEN_H
#define SCREEN_H
#endif

#include <SPFD5408_Adafruit_GFX.h>
#include <SPFD5408_Adafruit_TFTLCD.h>
#include <stdlib.h>
#include "menu/Menu.h"
#include "RF24.h"

#define LCD_CS A3
#define LCD_CD A2
#define LCD_WR A1
#define LCD_RD A0
#define LCD_RESET A4
#define SCREENUPDATE 100 // Update screen every x[ms]

#define BLACK     0x0000
#define BLUE      0x001F
#define RED       int(238/8*2048) + int(0) + int(0)
#define GREEN     0x07E0
#define CYAN      0x07FF
#define MAGENTA   0xF81F
#define YELLOW    0xFFE0
#define WHITE     0xFFFF
#define DARKGREEN int(255/8*2048) + int(255/4*32) + int(0/8)
#define DARKGRAY  int(70/8*2048)  + int(70/4*32)  + int(70/8)
#define LIGHTGRAY int(180/8*2048) + int(180/4*32) + int(180/8)
#define ORANGE    int(255/8*2048) + int(120/4*32) + int(0/8)
#define TEALBLUE  int(56/8*2048)  + int(142/4*32) + int(142/8)
#define TURQUISE  int(0/8*2048)  + int(245/4*32) + int(255/8)

class Screen {
private:
	Adafruit_TFTLCD *tft;
	RF24 *radio;
	uint8_t *channel_map;

	unsigned int update_counter = 0;
	char *controller_status = "";
	char *transmitter_status = "";
	uint8_t left_axis_pos[2] = { 0, 0 };
	uint8_t right_axis_pos[2] = { 0,0 };
	uint8_t ds4_battery_state = 0;
	uint8_t motor_state = -1;
	float battery_state = 0.0;


	int16_t plane_vec[12][6] = {
		{ 0, 30, 0, 0, -58, 0 }, // Rumpf
		{ 0, 30, -4, 0, -70, -4 }, // Rumpf
		{ -60, 0, 0, 60, 0, 0 }, // Fl¸gel front
		{ -60, -10, 0, 60, -10, 0 }, // Fl¸gel hinten
		{ -20, -70, 18, 20, -70, 18 }, // Side rudder
		{ 0, -70, -4, 0, -70, 18 }, // Height rudder
		{ 60, 0, 0, 60, -10, 0 },
		{ -60, 0, 0, -60, -10, 0},
		{ -20, -65, 18, 20, -65, 18},
		{ -20, -65, 18, -20, -70, 18},
		{ 20, -65, 18, 20, -70, 18},
		{ 0, -65, 18, 0, -58, 0}
	};
	int16_t line[4];
	int16_t origin[2] = { 160, 165 };
	double phi = 0, alpha = 0;
	double Rot[3][3];

	void initial_view();

public:
	String view = "control";
	Menu menu;
	Menu menu_channels;
	Menu menu_range;

	Screen(RF24 *radio, uint8_t *channel_map, uint8_t *axis_range_min, uint8_t *axis_range_max);
	void init();
	char* navigate(bool left, bool right, bool up, bool down, bool x, bool circle, bool options, bool l1, bool r1);
	void switch_view(String view);
	void print_peripheral_status(int id, char* type, char *message, bool force);
	void update_analog_axis(uint8_t axis, uint8_t x, uint8_t y, bool force);
	bool update();
	void update_motor(uint8_t motor);
	void update_battery(uint8_t ds4_battery, bool force);
	void update_voltage(float voltage, bool force);
	void draw_plane(double x, double y, double z);
};

menu.cpp

#include"Menu.h"

Menu::Menu() {}

Menu::Menu(Adafruit_TFTLCD *tft, RF24 *radio) {
	// Not default constructor with getting the
	// tfts address and creating the main options menu
	this->tft = tft;
	this->radio = radio;

	options = new Option[5];
	options[0] = Option("Channels", "enter");
	options[1] = Option("Axis Range", "enter");
	options[2] = Option("NRF24 PA Level", "select");
	options[3] = Option("NRF24 Data Rate", "select");
	options[4] = Option("Exit", "exit");
	options[0].active = true;
	options_size = 5;
	title = "Options";
}

Menu::Menu(Adafruit_TFTLCD *tft, uint8_t *channel_map) {
	// Not default constructor with getting the
	// tfts address and creating the options for the channel selection
	this->tft = tft;
	this->channel_map = channel_map;

	options = new Option[5];
	options[0] = Option("Channel 1", "channel");
	options[1] = Option("Channel 2", "channel");
	options[2] = Option("Channel 3", "channel");
	options[3] = Option("Channel 4", "channel");
	options[4] = Option("Exit", "exit");
	options[0].active = true;
	options_size = 5;
	title = "Channels";
}

Menu::Menu(Adafruit_TFTLCD *tft, uint8_t *axis_range_min, uint8_t *axis_range_max, String *view) {
	// Not default constructor with getting the
	// tfts address and creating the options for the axis range adjustment
	this->tft = tft;
	this->axis_range_min = axis_range_min;
	this->axis_range_max = axis_range_max;
	this->view = view;

	options = new Option[5];
	options[0] = Option("Channel 1", "range");
	options[1] = Option("Channel 2", "range");
	options[2] = Option("Channel 3", "range");
	options[3] = Option("Channel 4", "range");
	options[4] = Option("Exit", "exit");
	options[0].active = true;
	options_size = 5;
	title = "Axis Range";
}


void Menu::display(char *type) {
	// Creates the title on the tft display
	tft->fillScreen(BLACK);
	tft->setTextColor(ORANGE);
	tft->setTextSize(2);
	tft->setCursor(10, 10);
	tft->println(title);
	tft->setTextColor(WHITE);
	tft->setTextSize(1);
	init_main();
}

void Menu::init_main() {
	uint8_t index = 0;
	for (uint8_t i = 0; i < options_size; i++) {
		tft->setCursor(30, 60 + 30 * i);
		tft->println(options[i].getName());
		tft->drawLine(20, 78 + 30 * i, 300, 78 + 30 * i, DARKGRAY);
		if (options[i].active) {
			index = i;
		}
	}
	setMarker(index);
	printParameter(-1);
}

void Menu::next() {
	// Same as previous except of the direction
	int8_t index = getActiveElement();
	if (index > -1) {
		options[index].active = false;
		if (index == options_size - 1) {
			index = 0;
		}
		else {
			index = index + 1;
		}
		options[index].active = true;
	} 
	else {
		options[0].active = true;
		index = 0;
	}
	setMarker(index);
}

void Menu::previous() {
	// Set active for current element to false
	// Get previous index (modulo-like) and set
	// Next elements active variable to true
	int8_t index = getActiveElement();
	if (index > -1) {
		options[index].active = false;
		if (index == 0) {
			index = options_size - 1;
		}
		else {
			index = index - 1;
		}
		options[index].active = true;
	}
	else {
		options[0].active = true;
		index = 0;
	}
	setMarker(index);
}

void Menu::setMarker(uint8_t position) {
	tft->fillRect(0, 60, 20, 160, BLACK);
	tft->fillRect(5, 60 + 30 * position, 6, 6, ORANGE);
}

int8_t Menu::getActiveElement() {
	int8_t index = -1;
	for (int8_t i = 0; i < options_size; i++) {
		if (options[i].active) {
			index = i;
			break;
		}
	}
	return index;
}

char* Menu::execute(bool left, bool right, bool x, bool circle) {
	int8_t active = getActiveElement();
	char *action = "";
	if (options[active].getType() == "enter" && x) { // Select channels
		action = options[active].getName();
	}
	else if (options[active].getName() == "NRF24 PA Level") { // NRF24
		if (left) {
			char* state = options[active].previous();
			setRadioLevel(state);
			printParameter(active);
		}
		else if (right) {
			char* state = options[active].next();
			setRadioLevel(state);
			printParameter(active);
		}
	}
	else if (options[active].getName() == "NRF24 Data Rate") { // NRF24
		if (left) {
			char* state = options[active].previous();
			setRadioDatarate(state);
			printParameter(active);
		}
		else if (right) {
			char* state = options[active].next();
			setRadioDatarate(state);
			printParameter(active);
		}
	}
	else if (options[active].getType() == "channel") {
		if (left) {
			options[active].previous();
			setChannelMap(active, options[active].getActiveParameter());
			printParameter(active);
		}
		else if (right) {
			options[active].next();
			setChannelMap(active, options[active].getActiveParameter());
			printParameter(active);
		}
	}
	else if (options[active].getType() == "range" && x) {
		channel = active;
		*view = "set_range";
		display_range_adjustment(active);
	}
	else if (options[active].getType() == "exit" && x) { // Exit
		action = "exit";
	}
	return action;
}

void Menu::printParameter(int8_t id) {
	if (id == -1) {
		for (uint8_t i = 0; i < options_size; i++) {
			if (options[i].getType() == "select" || options[i].getType() == "channel") {
				tft->fillRect(200, 60 + 30 * i, 100, 8, BLACK);
				tft->setCursor(200, 60 + 30 * i);
				tft->setTextColor(WHITE);
				tft->println(options[i].selectedParam());
			}
		}
	}
	else {
		if (options[id].getType() == "select" || options[id].getType() == "channel") {
			tft->fillRect(200, 60 + 30 * id, 100, 8, BLACK);
			tft->setCursor(200, 60 + 30 * id);
			tft->setTextColor(WHITE);
			tft->println(options[id].selectedParam());
		}
	}
}

void Menu::display_range_adjustment(uint8_t id) {
	tft->fillScreen(BLACK);
	tft->setCursor(10, 10);
	tft->setTextColor(ORANGE);
	tft->setTextSize(2);
	tft->println(options[id].getName());
	tft->drawLine(30, 220, 290, 220, WHITE);
	tft->drawLine(160, 90, 160, 160, WHITE);
	tft->drawCircle(160, 220, 120, WHITE);
	tft->fillRect(40, 221, 3, 19, BLACK);
	tft->fillRect(278, 221, 3, 19, BLACK);
	min_selected = false;
	range_display_chart(ORANGE);
	min_selected = true;
	range_display_chart(ORANGE);
	range_display_values(false);
}

void Menu::range_set(int8_t value) {
	range_display_values(true);
	range_display_chart(BLACK);
	if (min_selected) {
		axis_range_min[channel] = axis_range_min[channel] + value;
	}
	else {
		axis_range_max[channel] = axis_range_max[channel] + value;
	}
	range_display_chart(ORANGE);
	range_display_values(false);
}

void Menu::range_toggle() {
	min_selected = !min_selected;
	range_display_values(false);
}

void Menu::range_display_chart(uint16_t color) {
	float center = (axis_range_min[channel] + axis_range_max[channel]) / 2.0;
	if(min_selected)
		tft->drawLine(cos(PI - axis_range_min[channel] / 180.0*PI) * 119 + 160, -sin(PI - axis_range_min[channel] / 180.0*PI) * 119 + 220, 160, 220, color);
	else
		tft->drawLine(cos(PI - axis_range_max[channel] / 180.0*PI) * 119 + 160, -sin(PI - axis_range_max[channel] / 180.0*PI) * 119 + 220, 160, 220, color);
	tft->drawLine(cos(PI - center / 180.0*PI) * 59 + 160, -sin(PI - center / 180.0*PI) * 59 + 220, 160, 220, color);
}

void Menu::range_display_values(bool overwrite) {
	if (overwrite) {
		tft->setTextColor(BLACK);
		if (min_selected) {
			tft->setCursor(30, 100);
			tft->println(axis_range_min[channel]);
		}
		else {
			tft->setCursor(250, 100);
			tft->println(axis_range_max[channel]);
		}
	}
	else {
		if (min_selected)
			tft->setTextColor(WHITE);
		else
			tft->setTextColor(DARKGRAY);
		tft->setCursor(30, 100);
		tft->println(axis_range_min[channel]);
		if (min_selected)
			tft->setTextColor(DARKGRAY);
		else
			tft->setTextColor(WHITE);
		tft->setCursor(250, 100);
		tft->println(axis_range_max[channel]);
	}
}

void Menu::setChannelMap(uint8_t id, uint8_t value) {
	if(id >= 0 && id <= 3)
		channel_map[id] = value;
}

void Menu::setRadioLevel(char *level) {
	if (level == "  1.5 mW") {
		radio->setPALevel(RF24_PA_MIN);
	}
	else if (level == "  6.3 mW") {
		radio->setPALevel(RF24_PA_LOW);
	}
	else if (level == " 25.1 mW") {
		radio->setPALevel(RF24_PA_HIGH);
	}
	else if (level == "100.0 mW") {
		radio->setPALevel(RF24_PA_MAX);
	}
	delay(20);
}

void Menu::setRadioDatarate(char *rate) { 
	if (rate == "250 kb/s") {
		radio->setDataRate(RF24_250KBPS);
	}
	else if (rate == "  1 Mb/s") {
		radio->setDataRate(RF24_1MBPS);
	}
	else if (rate == "  2 Mb/s") {
		radio->setDataRate(RF24_2MBPS);
	}
}

Menu.h

#ifndef MENU_H
#define MENU_H
#endif

#define DARKGRAY  int(70/8*2048)  + int(70/4*32)  + int(70/8)
#define BLACK     0x0000
#define ORANGE    int(255/8*2048) + int(120/4*32) + int(0/8)
#define WHITE     0xFFFF

#define ARRAY_SIZE(X) sizeof(X)/sizeof(X[0])

#include<Arduino.h>
#include<SPFD5408_Adafruit_GFX.h>
#include<SPFD5408_Adafruit_TFTLCD.h>
#include"../option/Option.h"
#include "RF24.h"

class Menu {
private:
	Adafruit_TFTLCD *tft;
	RF24 *radio;
	uint8_t *channel_map;
	uint8_t *axis_range_min;
	uint8_t *axis_range_max;

	char *current_menu = "base";
	String *view;
	Option *options;
	int options_size = 0;
	bool min_selected = true; // true = min, false = max
	uint8_t channel;

	void init_main();
	int8_t getActiveElement();
	void setMarker(uint8_t position);
	void setRadioLevel(char *level);
	void setRadioDatarate(char *rate);
	void setChannelMap(uint8_t id, uint8_t value);
	char *title;
	void display_range_adjustment(uint8_t id);
	void range_display_values(bool overwrite);
	void range_display_chart(uint16_t color);

public:
	Menu();
	Menu(Adafruit_TFTLCD *tft, RF24 *radio);
	Menu(Adafruit_TFTLCD *tft, uint8_t *channel_map);
	Menu(Adafruit_TFTLCD *tft, uint8_t *axis_range_min, uint8_t *axis_range_max, String *view);
	void display(char *type);
	void next();
	void previous();
	char *execute(bool left, bool right, bool x, bool circle);
	void printParameter(int8_t id);
	void range_toggle();
	void range_set(int8_t value);
};

option.cpp

#include"Option.h"

Option::Option() {}
Option::Option(char *name, char *type) {
	this->name = name;
	this->type = type;
	initialize();
}

char* Option::getName() {
	return this->name;
}

void Option::initialize() {
	//if (name == "NRF24 PA Level") {
	//Parameter params[4];
	//char name[6] = " Min ";
	if (name == "NRF24 PA Level") {
		params = new Parameter[4];
		params[0] = Parameter("  1.5 mW");
		params[1] = Parameter("  6.3 mW");
		params[2] = Parameter(" 25.1 mW");
		params[3] = Parameter("100.0 mW");
		params_size = 4;
		params[2].active = true;
	}
	else if (name == "NRF24 Data Rate") {
		params = new Parameter[3];
		params[0] = Parameter("250 kb/s");
		params[1] = Parameter("  1 Mb/s");
		params[2] = Parameter("  2 Mb/s");
		params_size = 3;
		params[0].active = true;
	}
	else if (type == "channel") {
		params = new Parameter[8];
		params[0] = Parameter("Left X");
		params[1] = Parameter("Left X Inv.");
		params[2] = Parameter("Left Y");
		params[3] = Parameter("Left Y Inv.");
		params[4] = Parameter("Right X");
		params[5] = Parameter("Right X Inv.");
		params[6] = Parameter("Right Y");
		params[7] = Parameter("Right Y Inv.");
		params_size = 8;
		params[0].active = true;
	}
}

char* Option::selectedParam() {
	int8_t index = getActiveParameter();
	char *output = "";
	if (index > -1 && index < params_size) {
		output = params[index].getName();
	}
	return output;
}

char* Option::getType() {
	return this->type;
}

char* Option::next() {
	int8_t index = getActiveParameter();
	if (index >= 0) {
		params[index].active = false;
		if (index < params_size - 1) {
			index = index + 1;
		}
		params[index].active = true;
	}
	else {
		params[0].active = true;
	}
	return params[index].getName();
}

char* Option::previous() {
	int8_t index = getActiveParameter();
	if (index >= 0) {
		params[index].active = false;
		if (index > 0) {
			index = index - 1;
		}
		params[index].active = true;
	}
	else {
		index = 0;
		params[0].active = true;
	}
	return params[index].getName();
}

int8_t Option::getActiveParameter() {
	int8_t index = -1;
	for (int8_t i = 0; i < params_size; i++) {
		if (params[i].active) {
			index = i;
			break;
		}
	}
	return index;
}

Option.h

#ifndef OPTION_H
#define OPTION_H
#endif

#include"parameter/Parameter.h"
#include<Arduino.h>

#define ARRAY_SIZE(X) sizeof(X)/sizeof(X[0])

class Option {
private:
	char *name;
	char *type;
	Parameter *params;
	uint8_t params_size;

	void initialize();


public:
	bool active = false;

	Option();
	Option(char *name, char *type);
	char* getName();
	char* getType();
	char* selectedParam();
	int8_t getActiveParameter();

	char* next();
	char* previous();
};

parameter.cpp

#include"Parameter.h"

Parameter::Parameter() {}

Parameter::Parameter(char *name) {
	this->name = name;
}

char* Parameter::getName() {
	return name;
}

Parameter.h

#ifndef PARAMETER_H
#define PARAMETER_H
#endif

#include<Arduino.h>

class Parameter {
private:
	char *name;

public:
	bool active = false;

	Parameter();
	Parameter(char *name);
	char* getName();
};

options.cpp

/*#include"Options.h"

Options::Options(Adafruit_TFTLCD *tft) {
	this->tft = tft;
}

Options::Options() {}

void Options::init() {
	//String params[4] = ;
	char *empty[] = { "test" };
	char *params[] = { "31", "32", "23", "13" };
	menu[0].init("Channels", "sub", empty, 0);
	menu[1].init("NRF24 PA Level", "select", params, 1);
	menu[2].init("Exit", "parent", empty, 0);
}

void Options::next() {
	if (active_option_view == "main") {
		for (uint8_t i = 0; i < ARRAY_SIZE(menu); i++) {
			if (menu[i].active) {
				menu[i].active = false;
				if (i == ARRAY_SIZE(menu) - 1)
					menu[0].active = true;
				else
					menu[i + 1].active = true;
				break;
			}
		}
	}
	setMarker();
}

void Options::previous() {
	if (active_option_view == "main") {
		for (uint8_t i = 0; i < ARRAY_SIZE(menu); i++) {
			if (menu[i].active) {
				menu[i].active = false;
				if (i == 0)
					menu[ARRAY_SIZE(menu) - 1].active = true;
				else
					menu[i - 1].active = true;
				break;
			}
		}
	}
	setMarker();
}

void Options::setMarker() {
	tft->fillRect(0, 60, 20, 200, BLACK);
	if (active_option_view == "main") {
		bool active = false;
		for (uint8_t i = 0; i < ARRAY_SIZE(menu); i++) {
			if (menu[i].active) {
				tft->fillRect(5, 60 + 30 * i, 10, 10, ORANGE);
				active = true;
				break;
			}
		}
		if (!active) {
			menu[0].active = true;
			setMarker();
		}
	}
}

void Options::print(char *type) {
	active_option_view = type;
	tft->fillScreen(BLACK);
	tft->setTextColor(ORANGE);
	tft->setTextSize(2);
	tft->setCursor(10, 10);
	tft->println("Options");
	tft->setTextColor(WHITE);
	tft->setTextSize(1.6);

	if (active_option_view == "main") {
		for (uint8_t i = 0; i < ARRAY_SIZE(menu); i++) {
			tft->setCursor(40, 60 + 30 * i);
			tft->println(menu[i].name);
			tft->drawLine(20, 78 + 30 * i, 300, 78 + 30 * i, DARKGRAY);

			//if(menu[i].type == "select")
			if (menu[i].type == "select") {
				tft->setCursor(220, 60 + 30 * i);
				Serial.begin(9600);
				Serial.println(menu[i].name);
				//tft->print(menu[i].params[0]);
			}
		}
		setMarker();
	}
	else {
		tft->setCursor(20, 60);
		tft->println("Option menu not found");
	}
}*/

Options.h

/*#ifndef OPTIONS_H
#define OPTIONS_H
#endif

#define DARKGRAY  int(70/8*2048)  + int(70/4*32)  + int(70/8)
#define BLACK     0x0000
#define ORANGE    int(255/8*2048) + int(120/4*32) + int(0/8)
#define WHITE     0xFFFF

#define ARRAY_SIZE(X) sizeof(X)/sizeof(X[0])

#include<SPFD5408_Adafruit_GFX.h>
#include<SPFD5408_Adafruit_TFTLCD.h>
#include"../option/Option.h"

//#include <StandardCplusplus.h>
//#include <serstream>
//#include <string>
//#include <vector>
//#include <iterator>

class Options {
private:
	typedef struct params {
		int id;
		String name;
	}  parameter_t;

	typedef struct option{
		int id;
		String name;
		parameter_t params[];
	} options_t;

	options_t *menu;
	Option menu[3];
	Adafruit_TFTLCD *tft;
	char *active_option_view;


	void setMarker();

public:
	Options();
	Options(Adafruit_TFTLCD *tft);

	void next();
	void previous();

	void print(char *type);
	void init();
};*/

Hi,
What version IDE are you using?
What OS are you using?

Thanks.. Tom... :smiley: :+1: :coffee: :australia:

I'm using the Arduino IDE and I am on both Mac OS and Windows OS

What version?

Thanks.. Tom.. :smiley: :+1: :coffee: :australia:

I'm using version 2.1.0

What happens when you use 1.8.19 version, the non internet dependent IDE?

Thanks Tom... :smiley: :+1: :coffee: :australia:

i was able to compile you code after making some changes

at least one cause of problems is that in many cases you put headers files in different directories and weren't consistent with which directory.

../option/Option.h
parameter/Parameter.h

The sloppy use of upper/lower case letters in the file names will cause problems on any operating system that is case sensitive.

As I mentioned in a previous reply, the Arduino IDE needs for the custom libraries to be located in a directory names "src", then it will perform recursive compilation for all the subdirectories within each library.

Even when the code compiles successfully, the compiler generates numerous warnings. The code makes extensive use of comparing a char* to a string constant, and passes string constants to functions that take a char* instead of a const char*.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.