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
3
  • you subtract two unsigned types so the result is stored in an unsigned temporary too. try abs((int16_t) value - this->value)
    – Juraj
    Mar 25 at 10:16
  • 1
    @Juraj: It should be either abs((int16_t) (value - this->value)) or abs((int16_t) value - (int16_t) this->value). Or, better yet, make everything signed. Otherwise, if only one term of the subtraction is signed, the unsigned type wins the “usual arithmetic conversions” game. Mar 25 at 11:17
  • @EdgarBonet Thank you, the weird behaviour was caused by uint16. I tested running the code (with uint) in VisualStudio and there it worked. Do you know why it worked in VS/Win11 but not in Arduino? Is the c++ version different? Compiler? And why didn't the compiler warn me? Ps, I am a complete beginner in c++
    – David
    Mar 25 at 12:55

1 Answer 1

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).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.