B4R Question [SOLVED] Inline C Error using plain C external modules

rwblinn

Well-Known Member
Licensed User
Longtime User
Trying to use these external modules, which are written in plain C.
When linking in B4R, "undefined reference errors" occur because the c source files are being converted to cpp files.
Have flagged in Inline C that the functions are c code with extern "C" blocks = see below snippet.

Using the Arduino IDE 1.8.13 no issues compiling & linking, i.e. sketch is running ok.

Q: How to link external modules written in plain C.

Error Messages:
... b4r_main.cpp:48: undefined reference to `tf_io4_v2_set_selected_value'
... Local\Temp\ccK8QaUj.ltrans0.ltrans.o: In function `io4_init':
... b4r_main.cpp:38: undefined reference to `tf_hal_arduino_init'

Inline C Snippet:
' Inline code setting up & use TF functions
' Tested first with Arduino IDE
#If C
// Tinkerforge
#include "src/hal_arduino/hal_arduino.h"
#include "src/bindings/hal_common.h"
#include "src/bindings/bricklet_io4_v2.h"
#ifdef __cplusplus
extern "C" {
#endif
// Define port connected Arduino CS pin: UNO=#10, MEGA=#53
TF_Port ports[1] = {
{
  .chip_select_pin=53,
  .port_name='A'
}
};
TF_HalContext hal;
TF_IO4V2 io;
char* IO4_UID = "G4d";
int IO4_CH_LED = 0;

void io4_init (B4R::Object* o) {
b4r_main::_tfrc = tf_hal_arduino_init(&hal, ports, sizeof(ports)/sizeof(ports[0]));
if (b4r_main::_tfrc < 0)
return
b4r_main::_tfrc = tf_io4_v2_create(&io, IO4_UID, &hal);
if (b4r_main::_tfrc < 0)
return
tf_io4_v2_set_configuration(&io, IO4_CH_LED, 'o', false);
}

void io4_set_state(B4R::Object* o) {
  tf_io4_v2_set_selected_value(&io, IO4_CH_LED, b4r_main::_state);
}

#ifdef __cplusplus
}
#endif
#End If
 
Last edited:

rwblinn

Well-Known Member
Licensed User
Longtime User
Explored further:
The src.ino is compiled and linked if the required modules (include statement: #include src/…) are copied to a subfolder src of the Objects folder:
B4X:
…\Objects\src.ino etc
…\Object\src\bindings\...
…\Object\src\hal_arduino\...

But the content of the folder src and its subfolders is cleared prior compiling (F5) - although the folder structure remains (../src, ../src/bindings …).
I do not want to use hardcoded path definition as the modules use relative path.
Q: Is there an option to disable clearing the src subfolder of the Objects folder?
Q: What is the function of the Files folder?

Due to the complexity of the modules, it is not planned to create a library.

Any other ideas appreciated.
 
Upvote 0

rwblinn

Well-Known Member
Licensed User
Longtime User
SOLVED = Thanks for the advice.

Solution
  • Created a placeholder library rTinkerforgeArduino
  • Copied the Tinkerforge C/C++ Bindings to the library folder
  • Removed the relative path from the files hal_arduino.h, .cpp (2 entries, need to ensure to remove again when the bindings are updated)
  • B4R program Inline C include reference set, i.e. #include "hal_arduino.h", #include "bricklet_io4_v2.h"
  • Created simple B4R test program = IO-4 Bricklet Channel 0 (output) with LED switch ON/Off via serial line = working fine
Next Steps
  • create a more complex program using single io4-bricklet with callbacks handling inputs
  • remove magic numbers & strings from Inline C = use B4RObjects
  • multiple bricklets connected to a hat zero bricklet connected to an Arduino MEGA
  • consider enhancing the rTinkerforgeArduino library
For those interested, the B4R program:



LED Serial Control:
Sub Process_Globals
    Public Serial1 As Serial
    Dim tfArduino As TinkerforgeArduino
    Dim AStream As AsyncStreams
    Dim ledState As Boolean = False
End Sub

Private Sub AppStart
    Serial1.Initialize(115200)
    Log("AppStart")
    tfArduino.Initialize
    AStream.Initialize(Serial1.Stream, "AStream_NewData", "AStream_Error")
    RunNative("io4_init", Null)
    Delay(100)
    RunNative("io4_set_state", Null)
    Delay(100)
End Sub

Sub AStream_NewData (Buffer() As Byte)
    Dim BC As ByteConverter
    Dim cmd As String = BC.StringFromBytes(Buffer)
    Log("Command:", cmd)
    If cmd == "0" Then ledState = False
    If cmd == "1" Then ledState = True
    RunNative("io4_set_state", Null)
End Sub

Sub AStream_Error
    Log("AStream Error")
End Sub

' Inline code setting up & use TF functions
#If C
// Tinkerforge Bindings
#include "hal_arduino.h"
#include "bricklet_io4_v2.h"

// Define port connected Arduino CS pin: UNO=#10, MEGA=#53
TF_Port ports[1] = {{.chip_select_pin=53, .port_name='A'}};

TF_HalContext hal;
TF_IO4V2 io;
char* IO4_UID = "G4d";
int IO4_CH_LED = 0;

int io4_init (B4R::Object* o) {
    ::Serial.println("io4_init: start");
    int rc;
    rc = tf_hal_arduino_init(&hal, ports, sizeof(ports)/sizeof(ports[0]));
    if (rc < 0) {
        ::Serial.println("io4_init: error hal init");
        return rc;
    }
    // ::Serial.println("io4_init: hal init done");
    rc = tf_io4_v2_create(&io, IO4_UID, &hal);
    if (rc < 0) {
        ::Serial.println("io4_init: error creating io4");
        return rc;
    }
    // ::Serial.println("io4_init: io4 created");
    tf_io4_v2_set_configuration(&io, IO4_CH_LED, 'o', true);
    ::Serial.println("io4_init: done");
    return rc;
}

int io4_set_state(B4R::Object* o) {
    ::Serial.print("io4_set_state: start: ");
    ::Serial.println(b4r_main::_ledstate, DEC);
    int rc = tf_io4_v2_set_selected_value(&io, IO4_CH_LED, b4r_main::_ledstate);
    ::Serial.println("io4_set_state: done");
    return rc;
}

#End If
 
Upvote 0
Top