EncoderTool Problems

fdaniels

Well-known member
I just wanted to start a new Sketch, copying definitions for Hardware and Variables from an older Sketch and - voila - it doesnt work.

It boils down to this:

Code:
#include "EncoderTool.h"
using namespace EncoderTool;

Encoder encoder;

int value=0;

void setup() {
  // put your setup code here, to run once:
  encoder.begin(14,15);
  encoder.setValue(0);
}

void loop() {
   //put your main code here, to run repeatedly:
   value = encoder.getValue();
}

Gives this Error:


Arduino: 1.8.13 (Windows 10), TD: 1.53, Board: "Teensy 3.2 / 3.1, Serial, 96 MHz (overclock), Faster, US English"

c:/program files (x86)/arduino/hardware/tools/arm/bin/../lib/gcc/arm-none-eabi/5.4.1/../../../../arm-none-eabi/lib/armv7e-m\libc.a(lib_a-writer.o): In function `_write_r':

writer.c:(.text._write_r+0x12): undefined reference to `_write'

collect2.exe: error: ld returned 1 exit status

Error compiling for board Teensy 3.2 / 3.1.


while changing " value = encoder.getValue();" to "Serial.println( encoder.getValue());" like below compiles fine.

Code:
#include "EncoderTool.h"
using namespace EncoderTool;

Encoder encoder;

int value=0;

void setup() {
  // put your setup code here, to run once:
  encoder.begin(14,15);
  encoder.setValue(0);
}

void loop() {
   //put your main code here, to run repeatedly:
  Serial.println( encoder.getValue());
}

Any Ideas what i am doing wrong? Im a bit puzzled as i used these Lines in allmost all sketches before and all of these compile just fine.
 
Your are doing nothing wrong. I can reproduce it but it is super weird. Something seems to try to use some c-library function which writes to stdout. Those functions need '_write' defined to do this. But, as usual in embedded systems _write is not defined. I'll need to dig into this in more detail.

Following quick workarounds:

  • As soon as you use Serial.println("something") the error goes away. (this is probably the reason why nobody noticed the problem so far)
  • The T4.1 core works without problem (need to look for the differences, maybe the T4.1 core lib defines '_write' somewhere
  • If you disable std::function callbacks (uncomment line 4 in the config file) it works


Code:
void loop() {
   val = encoder.getValue();
   Serial.println("weird");      // errror if you uncomment this line
}
 
Puh - i thought i am to dumb to understand.....

I can confirm, first calling a Serial.println("Irgendwas"); is a workaround. I got a bit crazy, because i just copied + pasted these lines from another Sketch and it works in the original Sketch flawless. If you like i can send you the whole Code as Mail, so you can have a look (its ab "bit" untidy and looong, so im not too keen to post it here).

Just checked:
Code:
#include "EncoderTool.h"
using namespace EncoderTool;

Encoder encoder;

int value=0;

void setup() {
  // put your setup code here, to run once:
  encoder.begin(14,15);
  encoder.setValue(0);
 
}

void loop() {
   //put your main code here, to run repeatedly:
   value= encoder.getValue();
    
}

compiles fine for Teensy LC + 4.0
 
Both Teensy 3 & 4 define _write() in Print.cpp.

My best guess is something we're doing somewhere in the core library is always linking in the Print class, and nobody has cared because we have megabytes of flash on Teensy 4. And Teensy 4 has only existed for a little over 1 year, so there hasn't been a lot of time for anyone to really worry minor issues where the only consequence is extra flash used.

But Teensy LC has much less memory, and has been around for several years. Quite a lot of work as gone into making sure we don't accidentally force unneeded code to get always linked in.

The really mysterious part (to me) is why would #include <functional> and use of the std:function templates result in something wanting to call _write() ?
 
I'm a little worried about this, because on my long & low-priority TODO list is converting attachInterrupt() and EventResponder to use std::function. Maybe not such a great idea afterall?
 
Both Teensy 3 & 4 define _write() in Print.cpp.

Yes, I've seen this. I also saw this over the part in setup.c where you define the other syscalls.
Code:
// syscall functions need to be in the same C file as the entry point "ResetVector"
// otherwise the linker will discard them in some cases.

#include <errno.h>

// from the linker script
extern unsigned long _heap_start;
extern unsigned long _heap_end;
//.....

I wonder if moving the _write definition would fix it.
 
I wonder if moving the _write definition would fix it.

If I move the definiton of _write from print.cpp to mk20dx12.c above the definition of _read, it works:
Code:
__attribute__((weak))
int _write(int file, char *ptr, int len)
{
	//((class Print *)file)->write((uint8_t *)ptr, len);
	//return len;
	return 0;
}

Alternatively, if you put this
Code:
extern "C" {
  int _write( int handle, char *buf, int count )
  {
      return Serial.write(buf, count);
  }
}
in some c++ file it gets linked and you can use the standard printf to print on Serial (if this is desirable at all).


I'm a little worried about this, because on my long & low-priority TODO list is converting attachInterrupt() and EventResponder to use std::function. Maybe not such a great idea afterall?
I'm using it all the time, never had issues with it since you fixed the linker script and the link sequence in 1.53
 
Last edited:
Does the other code print something to serial?

Nope, heres a snippet of the Code from a Sketch where it works:

Code:
pinMode(stopLEDpin, OUTPUT);


  analogWriteResolution(12);

  encoder.begin(encoderA,encoderB);  // A=0, B=1
  encoder.setValue(0);
  encOldValue = encoder.getValue(); // get current value

   // SETUP ACCUMULATOR TIMER
  //stepTimer.begin(updateOutputs, 250); 

  // SETUP BUTTON INTERRUPT
  //attachInterrupt(digitalPinToInterrupt(runButtonPin), runButtonISR, LOW);

  //TEMPOARRAY UPDATE
  recalculateTempo();
  
  // SETUP SERIAL MONITOR 
  Serial.begin(9600); 
  displaySplashScreen();

So im starting the Encoder before even starting Serial, and it works there flawless.
 
@fdaniels: I uploaded a fixed version which works here. Can you give it a try? (it implements the missing _write() which is a bit rude but works)

@Paul:
I'm a little worried about this, because on my long & low-priority TODO list is converting attachInterrupt() and EventResponder to use std::function. Maybe not such a great idea afterall?
So was I. To test it I placed a while(1){} in _write() which would effectively halt the processor, which did not happen... Looks like it never really calls _write, it just needs it to keep the linker happy.

BTW: I have an implementation of attachInterrupt() using std::function instead of the plain function pointer callback. In case you get bored and want to play around: https://github.com/luni64/TeensyHelpers :)
 
Last edited:
@fdaniels: I uploaded a fixed version which works here. Can you give it a try? (it implements the missing _write() which is a bit rude but works)

Adding EncoderTool-master from today to my /Dokumente/Arduino/Libraries folder in addition to EncoderTool-2.2.1 now compiles the Code in question:
Code:
#include "EncoderTool.h"
using namespace EncoderTool;

Encoder encoder;

int value=0;

void setup() {
  // put your setup code here, to run once:
  encoder.begin(14,15);
  encoder.setValue(0);
 
 
 
}

void loop() {
   //put your main code here, to run repeatedly:
  //Serial.println( encoder.getValue());
  value=encoder.getValue();
  
}

gives this:
Sketch uses 79376 bytes (30%) of program storage space. Maximum is 262144 bytes.
Global variables use 5556 bytes (8%) of dynamic memory, leaving 59980 bytes for local variables. Maximum is 65536 bytes.


Also, it compiles the old sketches flawless.

As far as my problem is concerned i'd say "problem solved" but i hope i didnt find something more worrying...
If i can be of any help, let me know.

Many thanks for diving into this!!!!
 
Great. Let me know if you find something else.

i hope i didnt find something more worrying...
No, just some things unthought of so far. Would be boring if everything just worked :).
 
Next Problem: the new Library doesnt compile older Sketches properly anymore for the LC:

Code:
#include "EncoderTool.h"
using namespace EncoderTool;

Encoder encoder;

int value=0;

void setup() {
  // put your setup code here, to run once:
  encoder.begin(11,12);
   encoder.setValue(0);
  
  //pinMode(14, INPUT_PULLUP);
 
}

void loop() {
   //put your main code here, to run repeatedly:
  //Serial.println( encoder.getValue());
  value=encoder.getValue();
  Serial.println(value);

  //if (digitalRead(14)==1){Serial.println("BUTTON");}
  
}

produces this:

Arduino: 1.8.13 (Windows 10), TD: 1.53, Board: "Teensy LC, Serial, 48 MHz, Smallest Code, US English"

C:\Users\danie\Documents\Arduino\libraries\EncoderTool-master\src\EncoderBase.cpp:196:1: error: expected declaration before '}' token

}

^

Multiple libraries were found for "EncoderTool.h"

Used: C:\Users\danie\Documents\Arduino\libraries\EncoderTool-master

Not used: C:\Users\danie\Documents\Arduino\libraries\EncoderTool-2.2.1

Error compiling for board Teensy LC.


This report would have more information with
"Show verbose output during compilation"
option enabled in File -> Preferences.


While exactly the same Code compiles for 3.2...
 
Ups, can you move the #endif after the closing bracket in EncoderBase.cpp (line 196). The #endif should be the last statement in the file. I need to leave right now, I'll fix it later today
 
Thanks for your superquick response!!
Will try this asap later - ill have to leave as well getting some things we missed yesterday for xMas.... ;-)
 
Will try this asap later - ill have to leave as well getting some things we missed yesterday for xMas.... ;-)
Yes, wife and daughter made me go in the forest to cut a tree :)
 
Picture please!

Well, its more like a baby tree and I had a hard time mounting it but it is a self cut tree after all ;)

IMG_20201223_145653.jpg
 
I uploaded a fix for the wrong conditional to the master branch of the lib.

BTW: Does anybody know of a working unit test framework for embedded projects? The growing number of boards makes proper testing of libraries a nightmare. Something more automated would be a real timesaver.
 
Back
Top