V 2.3.0
Arduino Uno / ESP-12F
While porting an sketch from Arduino it mysteriously crashed. I isolated the problem to the map() function when the arrays are empty. Once I fill it it works as it should.
The sketch is working without problems on Arduino Uno (returning -1 for each value) but uploaded to the ESP it crashes.
(Background: I need empty calibration data which will be filled later once sensors are read)
Minimal Sketch part to show it:
int FeuchtigkeitRAW[16] = {};
int FeuchtigkeitMAP[16] = {};
int FeuchtigkeitCALIB[32] = {};
void MapPots()
{
for (byte i = 0; i < 16; i++)
{
FeuchtigkeitMAP[i] = map(FeuchtigkeitRAW[i], FeuchtigkeitCALIB[i], FeuchtigkeitCALIB[i + 16], 0, 100);
}
}
void setup()
{
Serial.begin(115200);
}
void loop()
{
MapPots();
Serial.print("Wert ");
for (byte i = 0; i < 16; i++)
{
Serial.print(FeuchtigkeitMAP[i]);
Serial.print(" ");
}
Serial.print("\n");
delay(1000);
}
I like this one as I can ask about exception cause 6 - IntegerDivideByZeroCause :smile:
Adruino map() function is documented here including the formula.
I see implementation of identical formula for ESP here.
Passing non initialized int arguments to this function means effectively passing zero’s: map(0, 0, 0, 0, 100). Having such arguments will result in attempting to divide by zero :boom:
Handling of division by zero for integer numbers on various Arduino boards can be quickly checked with a short sketch:
void setup()
{
Serial.begin(115200);
int a, b;
a = 0;
b = 1 / a;
Serial.print("Result = ");
Serial.println(b);
}
void loop() {}
Results are as follows (Arduino IDE 1.6.9):
Arduino UNO (core ver. 1.5.11):

Arduino DUE / SAM (core ver.1.6.7) :

ESP8266 / Arduino (core ver. 2.3.0):

This explains why map(0, 0, 0, 0, 100) is working on Arduino UNO and crashing on ESP.
Personally I prefer to keep the ESP telling me that I am performing an illegal operation instead of potentially seeping this fact under carpet by returning -1 or 0 as a result. Having legitimate -1 inside some complex calculations may return seemingly legitimate result for invalid arguments.
Therefore such bug may stay in code unnoticed forever, while ESP will flag it immediately, before the code is even released.
Now, returning to my original question, does anybody know why division by zero is flagged with EXCCAUSE 0 - IllegalInstructionCause? Basing on the Exception Causes table I would expect 6 - IntegerDivideByZeroCause instead.
I assumed the same (divide by zero) but since -1 is in general "Error" you can check for it but crashing is no option that's acceptable for my opinion.
@krzychb It's slightly weird that you get an exception at PC=0x40106889. When I run the same sketch exception happens, as I would expect, at 0x4000dce5. Note 0x4000 part — this is in ROM, where _divsi3 (software integer division, 32-bit arguments) function is located. I am running git version though, maybe something is wrong in 2.3.0 (although the end result is the same).
Exception (0):
epc1=0x4000dce5 epc2=0x00000000 epc3=0x00000000 excvaddr=0x00000000 depc=0x00000000
ctx: cont
sp: 3ffef610 end: 3ffef7e0 offset: 01a0
>>>stack>>>
3ffef7b0: 3fffdad0 00000000 3ffee794 40201c44
3ffef7c0: feefeffe 00000000 3ffee7b0 4020211c
3ffef7d0: feefeffe feefeffe 3ffee7c0 40100108
<<<stack<<<
Regarding your question why we get exception 0 and not exception 6, it's pretty easy. CPU core inside the ESP8266 doesn't have hardware division instruction enabled. If it did, this instruction would cause exception 6. Because there is no division instruction, gcc calls aforementioned __divsi3 function each time it encounters division in program text. Looking at the source of this function, you'll see that authors decided to use ill, an illegal instruction opcode, to indicate division by zero.
https://github.com/jcmvbkbc/gcc-xtensa/blob/call0-4.8.2/libgcc/config/xtensa/lib1funcs.S#L634-L639
Overall, i don't consider the fact that integer division triggers exception an issue. Just as unaligned reads/writes work (albeit slowly) on some architectures and don't work on others. However, I think we should still check divisor against zero in map function.
I have just implemented this function in my code generator app (pfodDesignerV2). In that case I use the code like the following
long div = (in_max - in_min);
if (div == 0) div = 1;
long scale = (out_max - out_min) / div ; // do this first to avoid avoidable overflows
// from (x - in_min) * (out_max - out_min)
return (x - in_min) * scale + out_min;
"fixed" to return -1 in case of division by zero
Most helpful comment
@krzychb It's slightly weird that you get an exception at PC=0x40106889. When I run the same sketch exception happens, as I would expect, at
0x4000dce5. Note0x4000part — this is in ROM, where_divsi3(software integer division, 32-bit arguments) function is located. I am running git version though, maybe something is wrong in 2.3.0 (although the end result is the same).Regarding your question why we get exception 0 and not exception 6, it's pretty easy. CPU core inside the ESP8266 doesn't have hardware division instruction enabled. If it did, this instruction would cause exception 6. Because there is no division instruction, gcc calls aforementioned
__divsi3function each time it encounters division in program text. Looking at the source of this function, you'll see that authors decided to useill, an illegal instruction opcode, to indicate division by zero.https://github.com/jcmvbkbc/gcc-xtensa/blob/call0-4.8.2/libgcc/config/xtensa/lib1funcs.S#L634-L639
Overall, i don't consider the fact that integer division triggers exception an issue. Just as unaligned reads/writes work (albeit slowly) on some architectures and don't work on others. However, I think we should still check divisor against zero in
mapfunction.