Complete beginner to C/C++ here...
So, I'm trying to include the Arduino SoftwareSerial library in my otherwise vanilla AVR project (mostly out of laziness). However, when I build I get an "undefined reference" error.
Here's the code in question:
#include <avr/io.h>
#include <SoftwareSerial.h>
#define DEBUG_TX PB0
#define DEBUG_RX PB1
SoftwareSerial debug = SoftwareSerial(DEBUG_RX, DEBUG_TX);
int main() {
return 0;
}
And here's the command I'm using to build and the output:
avr-g++ -Ilib -I/usr/share/arduino/libraries/SoftwareSerial -I/usr/share/arduino/hardware/arduino/cores/arduino -g -Os -mmcu=atmega328p -Wall *.cpp -o example.bin -DF_CPU=8000000L
/tmp/ccsvCELp.o: In function `_GLOBAL__sub_I_debug':
/home/ksoviero/VS Code/Example/main.cpp:8: undefined reference to `SoftwareSerial::SoftwareSerial(unsigned char, unsigned char, bool)'
/tmp/ccsvCELp.o: In function `_GLOBAL__sub_D_debug':
/home/ksoviero/VS Code/Example/main.cpp:(.text.exit+0x4): undefined reference to `SoftwareSerial::~SoftwareSerial()'
collect2: error: ld returned 1 exit status
make: *** [Makefile:13: build] Error 1
So, without resorting to some premade tool like arduino-mk, how would I make this work manually? I want to learn how this works, not just make it work for the sake of working.
Thanks!
And here's the command I'm using to build and the output:
avr-g++ -Ilib -I/usr/share/arduino/libraries/SoftwareSerial -I/usr/share/arduino/hardware/arduino/cores/arduino -g -Os -mmcu=atmega328p -Wall *.cpp -o example.bin -DF_CPU=8000000L
So, without resorting to some premade tool like arduino-mk, how would I make this work manually? I want to learn how this works, not just make it work for the sake of working.
Thanks!
I am also new to Arduino, so this may not the right way but I was able to build your code with no error.
I just tired copying your code to main.c main.cpp with
#vi main.cpp
and then create a Makefile with
BOARD_TAG ?= pro328
TARGET = atmega328
CFLAGS += -g -Wall
MONITOR_PORT ?= (for your machine port)
ARDUINO_LIBS ?= SoftwareSerial
include /usr/share/arduino/Arduino.mk
and then type
# make
then it will generates binaries under `build-pro328` directory from your main.cpp.
I tried them after on Ubuntu with installing
# sudo apt install arduino arduino-mk
I was able build using the libcore.a from the above procedure.
I am writing while I am also learning how the Arduino build works.
This is the Makefile I created:
AVR_FREQ ?= 8000000L
MCU ?= atmega328p
CC = avr-gcc
CXX = avr-g++
OBJCOPY = avr-objcopy
CFLAGS += -g -Wall -DF_CPU=$(AVR_FREQ) -mmcu=$(MCU)
MONITOR_PORT ?= `echo your serial port on pc`
ARDUINO_LIBS ?= SoftwareSerial
INCLUDES := -I/usr/share/arduino/libraries/SoftwareSerial \
-I/usr/share/arduino/hardware/arduino/cores/arduino \
-I/usr/share/arduino/hardware/arduino/variants/standard
SRC := main.cpp
TARGET := example
BIN := $(TARGET).bin
HEX := $(TARGET).hex
all: $(BIN) $(HEX)
$(BIN): $(SRC)
$(CXX) $(INCLUDES) $(CFLAGS) $^ build-pro328/libcore.a -o $@
$(HEX): $(BIN)
$(OBJCOPY) -O ihex $< $@
upload: $(HEX)
avrdude -v -c arduino -p $(MCU) -P $(MONITOR_PORT) -b 115200 -U flash:w:$<
clean:
rm -f $(BIN) $(HEX)
Then type:
$ make
will generate example.bin and example.hex.
Two things I felft burdon for this manual makefile method.
First is that you have to add the path to include for libraries you use, such as
/usr/share/arduino/libraries/SoftwareSerial
to INCLUDE everytime for using other libraries. Those libraries are in /usr/share/arduino/libraries.
Second, building libcore.a without arduino-mk do now look easy.
But let me investigte later.
Would this help?
I got them to build together with libcore.a <- not working, user my later comment.
[EDIT] it was still using build-pro328/libcore.a which was built before with arduino-mk
The Makefile became bloated.
AVR_FREQ ?= 8000000L
MCU ?= atmega328p
CC = avr-gcc
CXX = avr-g++
OBJCOPY = avr-objcopy
AR = avr-ar
CFLAGS += -g -Wall -DF_CPU=$(AVR_FREQ) -mmcu=$(MCU)
MONITOR_PORT ?= `echo your serial port on pc`
ARDUINO_LIBS ?= SoftwareSerial
INCLUDES := -I/usr/share/arduino/libraries/SoftwareSerial \
-I/usr/share/arduino/hardware/arduino/cores/arduino \
-I/usr/share/arduino/hardware/arduino/variants/standard
SRC := main.cpp
TARGET := example
BIN := $(TARGET).bin
HEX := $(TARGET).hex
ARDUINO_DIR = /usr/share/arduino
ARDUINO_CORE_PATH = /usr/share/arduino/hardware/arduino/cores/arduino
ARDUINO_VAR_PATH = /usr/share/arduino/hardware/arduino/variants
VARIANT = standard
OBJDIR = .
CORE_LIB = $(OBJDIR)/libcore.a
all: $(BIN) $(HEX)
$(BIN): $(SRC) $(CORE_LIB)
$(CXX) $(INCLUDES) $(CFLAGS) $^ build-pro328/libcore.a -o $@
$(HEX): $(BIN)
$(OBJCOPY) -O ihex $< $@
upload: $(HEX)
avrdude -v -c arduino -p $(MCU) -P $(MONITOR_PORT) -b 115200 -U flash:w:$<
clean:
rm -f $(BIN) $(HEX)
## Bellow here is to build libcore.a
SYS_INCLUDES := $(foreach lib, $(SYS_LIBS), $(call get_library_includes,$(lib)))
USER_INCLUDES := $(foreach lib, $(USER_LIBS), $(call get_library_includes,$(lib)))
PLATFORM_INCLUDES := $(foreach lib, $(PLATFORM_LIBS), $(call get_library_includes,$(lib)))
CPPFLAGS += -mmcu=$(MCU) -DF_CPU=$(AVR_FREQ) -D__PROG_TYPES_COMPAT__ \
-I$(ARDUINO_CORE_PATH) -I$(ARDUINO_VAR_PATH)/$(VARIANT) \
$(SYS_INCLUDES) $(PLATFORM_INCLUDES) $(USER_INCLUDES) -Wall -ffunction-sections \
-fdata-sections
CORE_C_SRCS = $(wildcard $(ARDUINO_CORE_PATH)/*.c)
CORE_C_SRCS += $(wildcard $(ARDUINO_CORE_PATH)/avr-libc/*.c)
CORE_CPP_SRCS = $(wildcard $(ARDUINO_CORE_PATH)/*.cpp)
CORE_AS_SRCS = $(wildcard $(ARDUINO_CORE_PATH)/*.S)
CORE_OBJ_FILES = $(CORE_C_SRCS:.c=.c.o) $(CORE_CPP_SRCS:.cpp=.cpp.o) $(CORE_AS_SRCS:.S=.S.o)
MKDIR = mkdir -p
$(OBJDIR)/core/%.c.o: $(ARDUINO_CORE_PATH)/%.c $(COMMON_DEPS) | $(OBJDIR)
@$(MKDIR) $(dir $@)
$(CC) -MMD -c $(CPPFLAGS) $(CFLAGS) $< -o $@
$(OBJDIR)/core/%.cpp.o: $(ARDUINO_CORE_PATH)/%.cpp $(COMMON_DEPS) | $(OBJDIR)
@$(MKDIR) $(dir $@)
$(CXX) -MMD -c $(CPPFLAGS) $(CXXFLAGS) $< -o $@
$(OBJDIR)/core/%.S.o: $(ARDUINO_CORE_PATH)/%.S $(COMMON_DEPS) | $(OBJDIR)
@$(MKDIR) $(dir $@)
$(CC) -MMD -c $(CPPFLAGS) $(ASFLAGS) $< -o $@
CORE_OBJS = $(patsubst $(ARDUINO_CORE_PATH)/%, $(OBJDIR)/core/%,$(CORE_OBJ_FILES))
LIB_OBJS = $(patsubst $(ARDUINO_LIB_PATH)/%.c,$(OBJDIR)/libs/%.c.o,$(LIB_C_SRCS)) \
$(patsubst $(ARDUINO_LIB_PATH)/%.cpp,$(OBJDIR)/libs/%.cpp.o,$(LIB_CPP_SRCS)) \
$(patsubst $(ARDUINO_LIB_PATH)/%.S,$(OBJDIR)/libs/%.S.o,$(LIB_AS_SRCS))
USER_LIB_OBJS = $(patsubst $(USER_LIB_PATH)/%.cpp,$(OBJDIR)/userlibs/%.cpp.o,$(USER_LIB_CPP_SRCS)) \
$(patsubst $(USER_LIB_PATH)/%.c,$(OBJDIR)/userlibs/%.c.o,$(USER_LIB_C_SRCS)) \
$(patsubst $(USER_LIB_PATH)/%.S,$(OBJDIR)/userlibs/%.S.o,$(USER_LIB_AS_SRCS))
PLATFORM_LIB_OBJS := $(patsubst $(ARDUINO_PLATFORM_LIB_PATH)/%.cpp,$(OBJDIR)/platformlibs/%.cpp.o,$(PLATFORM_LIB_CPP_SRCS)) \
$(patsubst $(ARDUINO_PLATFORM_LIB_PATH)/%.c,$(OBJDIR)/platformlibs/%.c.o,$(PLATFORM_LIB_C_SRCS)) \
$(patsubst $(ARDUINO_PLATFORM_LIB_PATH)/%.S,$(OBJDIR)/platformlibs/%.S.o,$(PLATFORM_LIB_AS_SRCS))
$(CORE_LIB): $(CORE_OBJS) $(LIB_OBJS) $(PLATFORM_LIB_OBJS) $(USER_LIB_OBJS)
$(AR) rcs $@ $(CORE_OBJS) $(LIB_OBJS) $(PLATFORM_LIB_OBJS) $(USER_LIB_OBJS)
Then it will have:
$ ls
core example.bin example.hex libcore.a main.cpp Makefile
This is the fixed Makefile for building without arduino-mk.
However, how to build libcore.a manually is highly reverse engineered from /usr/share/arduino/Arduino.mk.
Please convert the spaces to tab of indentation inside the Makefile when copying bellow.
SHELL = /bin/bash -xue
AVR_FREQ ?= 8000000L
MCU ?= atmega328p
CC = avr-gcc
CXX = avr-g++
OBJCOPY = avr-objcopy
AR = avr-gcc-ar
CFLAGS += -g -Wall -DF_CPU=$(AVR_FREQ) -mmcu=$(MCU)
MONITOR_PORT ?= `echo your serial port on pc`
ARDUINO_LIBS ?= SoftwareSerial
INCLUDES := -I/usr/share/arduino/libraries/SoftwareSerial \
-I/usr/share/arduino/hardware/arduino/cores/arduino \
-I/usr/share/arduino/hardware/arduino/variants/standard
SRC := main.cpp
TARGET := example
BIN := $(TARGET).bin
HEX := $(TARGET).hex
ARDUINO_DIR = /usr/share/arduino
ARDUINO_CORE_PATH = /usr/share/arduino/hardware/arduino/cores/arduino
ARDUINO_VAR_PATH = /usr/share/arduino/hardware/arduino/variants
VARIANT = standard
OBJDIR = .
CORE_LIB = $(OBJDIR)/libcore.a
all: $(CORE_LIB) $(BIN) $(HEX)
$(BIN): $(SRC) $(CORE_LIB)
$(CXX) $(INCLUDES) $(CFLAGS) $^ -o $@
$(HEX): $(BIN)
$(OBJCOPY) -O ihex $< $@
upload: $(HEX)
avrdude -v -c arduino -p $(MCU) -P $(MONITOR_PORT) -b 115200 -U flash:w:$<
clean:
rm -fr $(BIN) $(HEX) $(CORE_LIB) $(OBJDIR)/core $(OBJDIR)/libs $(OBJDIR)/platformlibs
## Bellow here is to build libcore.a
CFLAGS_STD = -std=gnu11 -flto -fno-fat-lto-objects
CXXFLAGS_STD = -fpermissive -fno-exceptions -std=gnu++11 -fno-threadsafe-statics -flto
LOCAL_C_SRCS ?= $(wildcard *.c)
LOCAL_CPP_SRCS ?= $(wildcard *.cpp)
LOCAL_CC_SRCS ?= $(wildcard *.cc)
LOCAL_PDE_SRCS ?= $(wildcard *.pde)
LOCAL_INO_SRCS ?= $(wildcard *.ino)
LOCAL_AS_SRCS ?= $(wildcard *.S)
LOCAL_SRCS = $(LOCAL_C_SRCS) $(LOCAL_CPP_SRCS) \
$(LOCAL_CC_SRCS) $(LOCAL_PDE_SRCS) \
$(LOCAL_INO_SRCS) $(LOCAL_AS_SRCS)
ARDUINO_LIB_PATH = $(ARDUINO_DIR)/libraries
ARDUINO_LIBS += $(filter $(notdir $(wildcard $(ARDUINO_DIR)/libraries/*)), \
$(shell sed -ne 's/^ *\# *include *[<\"]\(.*\)\.h[>\"]/\1/p' $(LOCAL_SRCS)))
USER_LIBS := $(sort $(wildcard $(patsubst %,$(USER_LIB_PATH)/%,$(ARDUINO_LIBS))))
USER_LIB_NAMES := $(patsubst $(USER_LIB_PATH)/%,%,$(USER_LIBS))
SYS_LIBS := $(sort $(wildcard $(patsubst %,$(ARDUINO_LIB_PATH)/%,$(filter-out $(USER_LIB_NAMES),$(ARDUINO_LIBS)))))
SYS_LIB_NAMES := $(patsubst $(ARDUINO_LIB_PATH)/%,%,$(SYS_LIBS))
get_library_includes = $(if $(and $(wildcard $(1)/src), $(wildcard $(1)/library.properties)), \
-I$(1)/src, \
$(addprefix -I,$(1) $(wildcard $(1)/utility)))
SYS_INCLUDES := $(foreach lib, $(SYS_LIBS), $(call get_library_includes,$(lib)))
USER_INCLUDES := $(foreach lib, $(USER_LIBS), $(call get_library_includes,$(lib)))
CPPFLAGS += -mmcu=$(MCU) -DF_CPU=$(AVR_FREQ) -D__PROG_TYPES_COMPAT__ \
-I$(ARDUINO_CORE_PATH) -I$(ARDUINO_VAR_PATH)/$(VARIANT) \
$(SYS_INCLUDES) $(PLATFORM_INCLUDES) $(USER_INCLUDES) -Wall -ffunction-sections \
-fdata-sections -Os -fpermissive -fno-exceptions -std=gnu++11 -fno-threadsafe-statics -flto
CORE_C_SRCS = $(wildcard $(ARDUINO_CORE_PATH)/*.c)
CORE_C_SRCS += $(wildcard $(ARDUINO_CORE_PATH)/avr-libc/*.c)
CORE_CPP_SRCS = $(wildcard $(ARDUINO_CORE_PATH)/*.cpp)
CORE_AS_SRCS = $(wildcard $(ARDUINO_CORE_PATH)/*.S)
CORE_OBJ_FILES = $(CORE_C_SRCS:.c=.c.o) $(CORE_CPP_SRCS:.cpp=.cpp.o) $(CORE_AS_SRCS:.S=.S.o)
MKDIR = mkdir -p
$(OBJDIR)/core/%.c.o: $(ARDUINO_CORE_PATH)/%.c $(COMMON_DEPS) | $(OBJDIR)
@$(MKDIR) $(dir $@)
$(CC) -MMD -c $(CPPFLAGS) $(CFLAGS_STD) $< -o $@
$(OBJDIR)/core/%.cpp.o: $(ARDUINO_CORE_PATH)/%.cpp $(COMMON_DEPS) | $(OBJDIR)
@$(MKDIR) $(dir $@)
$(CXX) -MMD -c $(CPPFLAGS) $(CXXFLAGS_STD) $< -o $@
$(OBJDIR)/core/%.S.o: $(ARDUINO_CORE_PATH)/%.S $(COMMON_DEPS) | $(OBJDIR)
@$(MKDIR) $(dir $@)
$(CC) -MMD -c $(CPPFLAGS) $(ASFLAGS) $< -o $@
CORE_OBJS = $(patsubst $(ARDUINO_CORE_PATH)/%, $(OBJDIR)/core/%,$(CORE_OBJ_FILES))
get_library_files = $(if $(and $(wildcard $(1)/src), $(wildcard $(1)/library.properties)), \
$(call rwildcard,$(1)/src/,*.$(2)), \
$(wildcard $(1)/*.$(2) $(1)/utility/*.$(2)))
LIB_C_SRCS := $(foreach lib, $(SYS_LIBS), $(call get_library_files,$(lib),c))
LIB_CPP_SRCS := $(foreach lib, $(SYS_LIBS), $(call get_library_files,$(lib),cpp))
LIB_AS_SRCS := $(foreach lib, $(SYS_LIBS), $(call get_library_files,$(lib),S))
USER_LIB_CPP_SRCS := $(foreach lib, $(USER_LIBS), $(call get_library_files,$(lib),cpp))
USER_LIB_C_SRCS := $(foreach lib, $(USER_LIBS), $(call get_library_files,$(lib),c))
USER_LIB_AS_SRCS := $(foreach lib, $(USER_LIBS), $(call get_library_files,$(lib),S))
LIB_OBJS = $(patsubst $(ARDUINO_LIB_PATH)/%.c,$(OBJDIR)/libs/%.c.o,$(LIB_C_SRCS)) \
$(patsubst $(ARDUINO_LIB_PATH)/%.cpp,$(OBJDIR)/libs/%.cpp.o,$(LIB_CPP_SRCS)) \
$(patsubst $(ARDUINO_LIB_PATH)/%.S,$(OBJDIR)/libs/%.S.o,$(LIB_AS_SRCS))
$(OBJDIR)/libs/%.c.o: $(ARDUINO_LIB_PATH)/%.c
@$(MKDIR) $(dir $@)
$(CC) -MMD -c $(CPPFLAGS) $(CFLAGS_STD) $< -o $@
$(OBJDIR)/libs/%.cpp.o: $(ARDUINO_LIB_PATH)/%.cpp
@$(MKDIR) $(dir $@)
$(CXX) -MMD -c $(CPPFLAGS) $(CXXFLAGS_STD) $< -o $@
$(OBJDIR)/libs/%.S.o: $(ARDUINO_LIB_PATH)/%.S
@$(MKDIR) $(dir $@)
$(CC) -MMD -c $(CPPFLAGS) $(ASFLAGS) $< -o $@
$(CORE_LIB): $(CORE_OBJS) $(LIB_OBJS) $(PLATFORM_LIB_OBJS)
$(AR) rcs $@ $(CORE_OBJS) $(LIB_OBJS) $(PLATFORM_LIB_OBJS) $(USER_LIB_OBJS)
After the build, it should have these files.
$ ls
core example.hex libs Makefile example.bin libcore.a main.cpp