h2zero / n-able-Arduino

An arduino core for ARM based BLE devices supported by the NimBLE stack.
GNU Lesser General Public License v2.1
34 stars 12 forks source link

Linker error compiling with PlatformIO for micro:bit V2 #33

Open jhmaloney opened 7 months ago

jhmaloney commented 7 months ago

I get the following linker error when attempting to compile for a micro:bit V2: /Users/johnmaloney/.platformio/packages/toolchain-gccarmnoneeabi/bin/../lib/gcc/arm-none-eabi/9.3.1/../../../../arm-none-eabi/bin/ld: error: .pio/build/nable/firmware.elf uses VFP register arguments, .pio/build/nable/libFrameworkArduino.a(pulse_asm.S.o) does not

Here's my platformio.ini file entry:

[env:nable] platform = https://github.com/h2zero/platform-n-able.git#1.0.0 framework = arduino lib_deps = h2zero/NimBLE-Arduino@^1.4.0 board = BBCmicrobitV2 build_flags = -mfloat-abi=softfp

I added the "-mfloat-abi=softfp" in attempt to force it to use sofware floating point that did not help.

h2zero commented 7 months ago

Hello, I am not able to reproduce this using:

[env:nable]
platform = https://github.com/h2zero/platform-n-able.git#1.0.0
framework = arduino
lib_deps = h2zero/NimBLE-Arduino@^1.4.0
board = bbcmicrobitv2 ; This was changed because BBCmicrobitV2 is not valid for this platform
jhmaloney commented 7 months ago

Thanks! It's great that compilation works for you. There is probably something in my code that causing the problem.

I pasted that exact entry into my .platformio.ini file and I am still getting the error.

I'm running on MacOS and I'm running PlatformIO Core, version 6.1.11, which I believe is the latest.

Are the version numbers correct?

From the error message, it sounds as though something in my code might be trying to use hardware floating point registers. It shouldn't be doing that, since the micro:bit doesn't have an FPU. Is there a compiler switch I should add to force the compiler to use software floating point?

I'll try to isolate the problem to a single source file.

h2zero commented 7 months ago

Try deleting the .pio folder and compiling and empty sketch with just setup and loop defined.

jhmaloney commented 7 months ago

Yep, that succeeded! The problem must be somewhere in my code; I'll try to find it by process of elimination.

Where would be the best place to ask questions about running NimBLE + n-able-Arduino on the micro:bit V2? Questions include things like: "Which timers and other resources are used by NimBLE?" and "Can I perform Flash memory operations while running NimBLE?"

I've been using the Nordic Softdevice, which takes over a lot of the nRF hardware, including the Flash memory system and the interrupt system. I'm hoping that NimBLE is less intrusive.

h2zero commented 7 months ago

Best place to ask is here for sure as those questions are more related to system operations than BLE.

jhmaloney commented 7 months ago

Best place to ask is here for sure as those questions are more related to system operations than BLE.

Great! So, for the micro:bit v2 (nRF52833) here are my questions:

  1. Can I use the non-volatile memory controller (NRF_NVMC) to erase and write to Flash memory?
  2. Are there any restrictions on the interrupt system? (The Nordic Softdevice doesn't allow use of high-priority interrupts since it reserves those for the Bluetooth radio system.)
  3. Are there any restrictions on which timers I can use?
  4. Are there any non-obvious things I need to watch out for?

Thank you so much for creating the n-able-Arduino framework and making it work with PlatformIO. This could be a game changer for adding BLE to the MicroBlocks project.

FYI, I am the lead developer of MicroBlocks (https://microblocks.fun), a free, open-source educational blocks programming system for the micro:bit and dozens of other microcontrollers designed to introduce beginners to the joys of physical computing. MicroBlocks is built on the Arduino framework and we use PlatformIO to build it for over 50 different boards.

Thank you!

h2zero commented 7 months ago
  1. There is an API available for flash read and write, you can use either EEPROM or the underlying functions in here. the NRF_NVMC is also available through the nrfx mdk included in this repo.
  2. There aren't really any restrictions per-se but the BLE radio does have top priority (0) and probably should be alone in this regard unless the radio isn't being used.
  3. Yes, Timer0 is reserved for the BLE stack just as the softdevice did.
  4. Nothing that I can think of at the moment.

You're welcome, I hope you'll find it helpful and I'm glad to see it being put to good use.

jhmaloney commented 7 months ago

This is very helpful info. Thanks!

Another question: How much RAM does NimBLE require while it is running? I realize that probably varies depending on the application but what is the typical RAM requirement for a simple BLE peripheral?

h2zero commented 7 months ago

That would depend largely on how you configure things, but 12-18k would be a typical range.

jhmaloney commented 7 months ago

Thanks for answering the RAM question.

I've managed to reproduce the linker error. Try compiling the following:

`

include

void setup() { int pulseWidth = pulseIn(0, HIGH, 2000); } void loop() { } `

Looking at the source for pulse_asm.S, it appears that it was translated to assembly using the -mcpu=cortex-m0plus switch. Does that make the code incompatible with M4 processors that have hardware floating point?

Now that I've found the issue, I can work around it. I'm only using the pulseIn() function in one place in my code. But I thought you might want to know in case other folks run into this issue.

h2zero commented 7 months ago

Thanks, I'll have a look at this.

jhmaloney commented 7 months ago

I have another question. I now have MicroBlocks running under n-able-arduino with the NimBLE-Arduino library. However, it seems to restart every four seconds or so. Is that due to some sort of watchdog timer in FreeRTOS? If so, how do I prevent that from happening?

Never mind; I found the answer. I just needed to set CONFIG_WDT_TIMEOUT_SECONDS=0 to disable the watchdog timer.

h2zero commented 6 months ago

I'm curious what your code was doing that triggered this?

jhmaloney commented 6 months ago

I did not have a build_opt.h file so the watchdog timer setting defaulted to five seconds. Since I didn't even know about the watchdog timer (I obviously didn't read your documentation carefully!), I wasn't resetting it. The fix was to add a compiler switch to set it to 0 and thus disable it.

It might be good to make 0 be the default in case other folks run into this issue.

My application is a programming language interpreter like MicroPython so it's not the usual application. In fact, it's designed to run on bare-metal MCU's, so it has it's own internal task system. I know n-able-Arduino is built on FreeRTOS, so I restructured the MicroBlocks interpreter to return control to the "loop()" function every few hundred microsecs, which I assume is necessary to allow other RTOS tasks to run, including the BLE radio system. That design seems to be working. Once I disabled the watchdog timer I was able to run a BLE UART within MicroBlocks.

However, please let me know if there are things the MicroBlocks interpreter should be doing. For example: I know the code should use the thread-safe version of malloc. Fortunately, MicroBlocks only uses malloc in a very few places, so that's easy. As far as I can tell, MicroBlocks is not setting any interrupts to priority 0.

MicroBlocks does use the NVMC registers directly, which I know could block interrupts during Flash erase operations. Should I switch to the n-able-Arduino Flash API?

I changed the code to use Timer1 rather than Timer0. However, I think I read something suggesting that NimBLE now uses Timer5 on nRF52 chips, so perhaps I did not need to make that change.

jhmaloney commented 6 months ago

I should add, I'm finding the n-able-Arudino framework and the NimBLE library a joy to work with. The documentation is clear, the API's are well designed, and I can dig into the source code if I have questions.

This is a welcome change from my experience working with the Nordic Softdevice, which was one frustrating, non-obvious problem after another. Nordic assumes that people will use their SDK, so there is scanty documentation on how to use the Softdevice from the Arduino framework and virtually no examples. And, if you get stuck using the Softdevice, you can't even grep for things in the source code because it's a black box.

Thanks so much for creating n-able-Arduino!

jhmaloney commented 6 months ago

After doing: pTxCharacteristic->notify(true) is it possible to know when the notification has actually been sent and it is okay to call pTxCharacteristic->setValue() again on the characteristic?

I'm implementing a Nordic UART (starting from the example) and would like to maximize the outgoing bandwidth. The example does a 10 msec wait to ensure that the notification got sent. I tried decreasing that to 5 msecs but that caused lost data. It looks like the low-level code is posting an event when the attribute value has been transmitted but I can't figure out what I need to do at the application level to find out when that happens. Is there a flag I can test or an event I can catch?

Thanks!

h2zero commented 6 months ago

I restructured the MicroBlocks interpreter to return control to the "loop()" function every few hundred microsecs

You don't really need to do this unless using serialEventRun. BLE runs it's own tasks, so as long as they aren't starved of processing time then there should be no issues.

For example: I know the code should use the thread-safe version of malloc

The malloc and new functions in this core are routed through FreeRTOS calls that are thread safe: https://github.com/h2zero/n-able-Arduino/blob/master/cores/nRF5/libc/malloc.c

MicroBlocks does use the NVMC registers directly, which I know could block interrupts during Flash erase operations. Should I switch to the n-able-Arduino Flash API?

If your code is working I would say no, though they may make things easier to debug.

I should add, I'm finding the n-able-Arudino framework and the NimBLE library a joy to work with. 😄

Thanks so much for creating n-able-Arduino!

You're very welcome and thank you for the feedback!

After doing: pTxCharacteristic->notify(true) is it possible to know when the notification has actually been sent and it is okay to call pTxCharacteristic->setValue() again on the characteristic?

The characteristic value is copied to a buffer when notify is called, it's safe to change the value immediately after.

It looks like the low-level code is posting an event when the attribute value has been transmitted but I can't figure out what I need to do at the application level to find out when that happens. Is there a flag I can test or an event I can catch?

There is a callback that is called when the event happens, see example line

jhmaloney commented 6 months ago

Thank for the pointer to the example.

jhmaloney commented 5 months ago

Thanks for answer my questions. Things are working well now except that I had to comment out some code that used the Arduino pulseIn() function. (Code demoing the problem is in my comment in this thread on Nov 30.) Have you had a chance to look at that? Let me know if you have trouble reproducing the problem.

h2zero commented 5 months ago

I think we could borrow the solution from adafruit here: https://github.com/adafruit/Adafruit_nRF52_Arduino/blob/59a30d4665c75fcb1aabc09b4c40634ed96076a7/cores/nRF5/pulse.c

jhmaloney commented 5 months ago

That looks good. The nRF52 is fast so I think a C implementation should be precise enough for most uses of pulseIn(). It would certainly work for decoding signals from a DHT-11 temperature/humidity sensor, which is my immediate application.

Do you plan to add the Adafruit solution to the n-able-Arduino framework? That would be great!

If that will happen soon then I will wait for it. If not, I can create my own function based on the Adafruit code.

h2zero commented 5 months ago

If you'd like to copy that change and test it to work for your application I'd appreciate it. A PR would be most welcome if it's working