2

Analog pontentiometer connected to an Arduino. simplest possible setup. The goal is to send a message over Serial whenever the value changes.

I do not want to spam the Serial connection to much, so the value has to change by at least 20 (sensitivty threshold) before a message Packet is sent.

However, in reality a message Packet is sent even if the value changed only by 1. Why is sensitivity parameter ignored?

edit: After debugging I can see that it enters the first IF-statement in read(). The IF statement evaluates to 0 which is super weird. It shouldn't have entered that IF statement!

MagicPot.h:

#define MAGIC_POT_MAX_RAW_VALUE_10B     1023
#define MAGIC_POT_MAX_RAW_VALUE_12B     4095
#define MAX_RAW_VALUE_DEFAULT           (MAGIC_POT_MAX_RAW_VALUE_10B)

class MagicPot { public: typedef void (*callback_fn)(); MagicPot(uint8_t pin, uint16_t minRead = 0, uint16_t maxRead = MAX_RAW_VALUE_DEFAULT, uint16_t maxRawRead = MAX_RAW_VALUE_DEFAULT): pin(pin), minRead(minRead), maxRead(maxRead), maxRawRead(maxRawRead) {}; void begin(); bool read(uint8_t sensitivity = 5); uint16_t getValue(); uint16_t getRawValue(); uint8_t pin;

private:
    uint16_t minRead;
    uint16_t maxRead;
    uint16_t maxRawRead;
    callback_fn onChangeCallback;
    uint16_t value;
    uint16_t rawValue;

};

MagicPot.cpp:

void MagicPot::begin()
{
    this->onChangeCallback = nullptr;
    this->value = 0;
    this->rawValue = 0;
    pinMode(this->pin, INPUT);
    this->read(0);
}

bool MagicPot::read(uint8_t sensitivity) { this->rawValue = analogRead(this->pin);

uint16_t value = map(this->rawValue, 0, this->maxRawRead, this->minRead, this->maxRead);

if (abs(value - this->value) > sensitivity)
{
    this->value = value;
    return true;
}
else if (value < sensitivity && this->value != this->minRead)
{
    this->value = minRead;
    return true;
}
else if (value + sensitivity > maxRead && this->value != this->maxRead)
{
    this->value = this->maxRead;
    return true;
}
return false;

}

Arduino code:

MagicPot analogPots[] = 
{
    MagicPot(A0),
}

void setup() { for (MagicPot& x : analogPots) //arr only contains 1 element x.begin(); } void loop() { for (MagicPot& pot : analogPots) //arr only contains 1 element { if(true == pot.read(20)) { CPPacket* analogPotChangedPacket = new CPPacket(HWEvent::Switch, pot.pin, pot.getValue()); SendPacket( &myPacketSerial, analogPotChangedPacket); delete analogPotChangedPacket; } }

}

Actual Output: (read from computer(conn via Serial))

Type Error Target Value
Switch NONE 54 516
Switch NONE 54 515
Switch NONE 54 514
Switch NONE 54 513

Expected Output:

Type Error Target Value
Switch NONE 54 516
Switch NONE 54 496
Switch NONE 54 456
Switch NONE 54 436
David
  • 125
  • 5

1 Answers1

3

Consider this subtraction:

value - this->value

Like in any arithmetic operation, the operands undergo integral promotion, then usual arithmetic conversions. Oversimplifying a bit, “integral promotion” means that types smaller than int get promoted to int, whereas “usual arithmetic conversions” mean the smaller operand gets promoted to the type of the bigger one. If both types have the same size, the unsigned one wins.

On the Arduino Mega, and int is 16 bits. Since the operands of the subtraction are both uint16_t, the types are not changed by these implicit conversions. The subtraction is performed with the uint16_t type and wraps around modulo 216. If you expect −1, you get 65535.

If you cast one of the operands to int16_t, then it will be implicitly converted back to uint16_t by the usual arithmetic conversions, and you get the same result.

On your PC, an int is 32 bits, thus both operands get implicitly converted to int by integral promotion, and you get the expected signed result.

The simple solution to your problem is to turn all your variables to int16_t. I suggest you always use signed types by default, and revert to unsigned types when you do need the wraparound semantics of the arithmetic operations (it is useful sometimes).

Edgar Bonet
  • 45,094
  • 4
  • 42
  • 81