Overflowing uint_16T on purpose?

I am trying to get my head around a problem.

I am basically trying to get a motor to move to a desired position read from an analog sensor, in a closed loop fashion. This sensor is basically an hall effect sensor, which goes from 0 to 5V.

I am having an hard time setting speed, or rather calculating the distance from the desired position, if the sensor will cross the 0V position. Also I find hard to get around on how to hold the position (like a servo would do) when the desired position is around the 0V output of the sensor. This is because if I move a little bit over 0V, the sensor will output 5V.

I was thinking about mapping the values to an uint_16 and letting it overflow either way, but here int - Arduino Reference I read that it might give unpredictable results. (not the same variable type but still got me wondering).

Is this way of doing math around a circle ok?

How could I do simple math in a "circular" way?

tomy983:
I am trying to get my head around a problem.

I am basically trying to get a motor to move to a desired position read from an analog sensor, ...

tomy983:
..... This is because if I move a little bit over 0V, the sensor will output 5V.

confused... from what you wrote it sounds like you have an digital output (0V or 5V) not analog (ie any value between 0 and 5V depending on position)

are you sure your sensor is analog?

The most common way of doing 'circular' math is by using the modulo operator (%) where 'n' would be the rollover value so your statement would be,

x = (x +1) % n;

This would then count from 0 to n-1 and the restart from 0.

[EDIT]from 0 to n-1 - see post #10

sherzaad:
confused... from what you wrote it sounds like you have an digital output (0V or 5V) not analog (ie any value between 0 and 5V depending on position)

are you sure your sensor is analog?

Sorry, I did't really made myself clear enough.
My sensor is actually an analog angle sensor, with 5v supply voltage and 0 to 5V output. it allows for continuous rotation, which is what I need but eventually will lead to my problem.
I am actually reading the sensor via I2c using an ADC.

tomy983:
Sorry, I did't really made myself clear enough.
My sensor is actually an analog angle sensor, with 5v supply voltage and 0 to 5V output. it allows for continuous rotation, which is what I need but eventually will lead to my problem.
I am actually reading the sensor via I2c using an ADC.

OK so it is analog... good. What is the resolution of your ADC? Knowing that, you coud then apply @DKWatson suggestion! :wink:

DKWatson:
The most common way of doing 'circular' math is by using the modulo operator (%) where 'n' would be the rollover value so your statement would be,

x = (x +1) % n;

This would then count from 0 to n and the restart from 0.

Well, I had no idea about this operator. I will give it a try, looks exactly it might do the job (if i can figure out all the other math needed :slight_smile: )

I am wondering if uint16_t x = (0 - 1) % 5; will output 4
I have to give this a try.

Thank you.

sherzaad:
OK so it is analog... good. What is the resolution of your ADC? Knowing that, you coud then apply @DKWatson suggestion! :wink:

The ADC is an ADS1115
It has 16bit resolution with one of those being the sign, probably more than I need. I still have to look for a library or a function to filter a little the output (kalman?).

what I am now at is on the setup I make a little more than a full rotation to find the min and max values of the sensor (it seem to vary from using different PSU?) and then I map from 0 to 3600 (this would be 10x360).

I have other bits of code completed but is still mostly unusable...

tomy983:
what I am now at is on the setup I make a little more than a full rotation to find the min and max values of the sensor (it seem to vary from using different PSU?) and then I map from 0 to 3600 (this would be 10x360).

share your code. that's what we get a better understanding of what you have done so far and what you are trying to achieve.

IMHO, if your sensor and arduino share the same supply, use the RAW ADC value instead on converting it to voltage then you do not really need to worry about the PSU fluctuations

sherzaad:
share your code. that's what we get a better understanding of what you have done so far and what you are trying to achieve.

Right.
just the time to translate the comments in english. Still, at present it does nothing.

tomy983:
Right.
just the time to translate the comments in english. Still, at present it does nothing.

not here to judge. Just want to see how you put your words in code. that's the beauty of code, human language may vary but code remains the same. :slight_smile:

DKWatson:
x = (x +1) % n;

This would then count from 0 to n and the restart from 0.

It will count from 0 to n-1 and then restart from zero.

Pete

tomy983:
Well, I had no idea about this operator. I will give it a try, looks exactly it might do the job (if i can figure out all the other math needed :slight_smile: )

I am wondering if uint16_t x = (0 - 1) % 5; will output 4
I have to give this a try.

Thank you.

No, because 0 - 1 = 65535. 65535%5 = 0. (0-2)%5 will output 4.

Correct. See edit.

el_supremo:
It will count from 0 to n-1 and then restart from zero.

Pete

sherzaad:
not here to judge. Just want to see how you put your words in code. that's the beauty of code, human language may vary but code remains the same. :slight_smile:

I actually would be gladly judged and corrected.

This is the code I have now. Unfinished and mostly untested.
Is part of a machine that I designed and 90% build. The purpose of this machine is to bend wire in 3 dimension.
youtube video of concept machine

I was actually thinking about opensource the machine mechanic project (mostly 3d printed) and code, possibly with the intention to get some help, but I wouldn't know where to start....
If interested I can post a picture from 3d cad of the machine design.

The electronics are :
3 arduino nano, one for each axis. ( this design choice might be questionable but I preferred having one controller per axis. in my mind this way I can complete each axis as an individual software project. Since I am just a beginner I look at it as a less steep step. also the machine does only need to move one axis at a time).

1 arduino mega with tft lcd and sd card (still with the postman - this will be the master device, from which to select the actual piece to be bended and eventually modify the part... still no clue on all of this. will see).
it will send to each axis the new setpoint and wait for response to proceed with next axis.

  • first axis
    wire feeder. two stepper motors on same driver with 1000ppr encoder moved only by the wire. COMPLETED.

  • second axis
    rotation of head around wire. one stepper and one angle sensor read via I2c. full 360 rotation allowed. no endstops.

  • third axis
    actual bender. one reduced stepper and angle sensor as per previous axis. full 360 rotation allowed. no endstop. motor power and sensor reading via slipring for allowing full rotation. code to be written. possibly very similar to previous axis.

#include <Wire.h> //modified
#include <Adafruit_ADS1015.h> // modified
#include <FastPID.h>
#include <TimerOne.h>

int debug = 0; // debug

////// adafruit library is edited to set sample rate of ADS1115 to 860Hz 
////// Wire library is edidited for a frequency of 400kHz
Adafruit_ADS1115 ads(0x48); // Use this for the 16-bit version 
int IngressoADC = 0; // imposta canale adc sull'ADS1115

float Kp = 28, Ki = 0, Kd = 20.5, Hz = 10; // PID settings (to be modified)
int output_bits = 16; 
bool output_signed = true;
FastPID myPID(Kp, Ki, Kd, Hz, output_bits, output_signed);
const int PidCorrection = 2580; // PID correction output

volatile int setpoint = 0; // where my motor has to move to
volatile bool hold = 0; // hold in position or move

volatile int sensorValue = 0; // sensor reading
int sensorMAX = -32768; // max reading of sensor value
int sensorMIN = 32767; // max reading of sensor value
int diff_error = 30; // max accattable error for hold or positioning. (to be modified)

bool check = 0; // just a check
bool direzione = 0; // direction of stepper

///////////  motor pin definition
const byte ENA = 7; // enable pin
const byte DIR = 8; // direction pin 
const byte STEPpin = 9; // pulses pin

/////////// duty cycle and motor frequency
int DUTY = 300;
int PWM = 80; //(lower number higher frequency)

/////////// I2c address definition
int I2cAddress = 3;

void setup(void)
{
 ///////////  debug check
 pinMode(13, OUTPUT);
 Serial.begin(250000);

 //Wait for two seconds or till data is available on serial, 
 //whichever occurs first.
 while (Serial.available() == 0 && millis()<2000);
 if (Serial.available()>0){
 //If data is available, enter here.
 int test = Serial.read(); // Clear the input buffer
 Serial.println("DEBUG"); // Give feedback indicating mode
 debug = 1; // Enable debug
 digitalWrite(13, HIGH);
 }
 else{ // disable debug
 Serial.end();
 digitalWrite(13, LOW);  
 }

 ///////////   setup I2c
 Wire.begin(I2cAddress); // join i2c bus with address #2
 Wire.onReceive(receiveEvent); // register event function

 ////////////////  setup pid
 myPID.setOutputRange(80, 2500);

 ///////////// setup stepper output
 pinMode(DIR, OUTPUT); //direction
 pinMode(ENA, OUTPUT); //enable (active low)
 digitalWrite(ENA, HIGH); //motor off
 digitalWrite(DIR, HIGH); //set direction
 Timer1.initialize();
 Timer1.pwm(STEPpin, DUTY, PWM); //initialize stepper pulses (to be removed)
 Timer1.stop(); //stop stepper pulses

 ////////////  debug
 if (debug == 1) {
 if (myPID.err()) {
 Serial.println("There is a configuration error!");
 for (;;) {}
 }

 Serial.println("Getting single-ended readings from AIN0");
 Serial.println("ADC Range: 2/3x gain +/- 6.144V  1 bit = 0.1875mV");

 }

 /////////// setup ADS1115
 ads.setGain(GAIN_TWOTHIRDS);  // set adc to 2/3
 ads.begin();

 /////////// find min ad max sensor values rotating at low speed
 digitalWrite(ENA, LOW);
 Timer1.pwm(STEPpin, DUTY, 4500);
 for (int i = 0; i < 32300; i++){
  CalibrazioneSensore();
 }
 digitalWrite(ENA, HIGH); //motor off
 ////////////  debug
 if (debug == 1) {
 Serial.println("valore massimo sensore");
 Serial.println(sensorMAX);
 Serial.println("valore minimo sensore");
 Serial.println(sensorMIN);
 }

}

void loop(void)
{
 ///////// find PWM output for stepper using PID
 uint16_t output = myPID.step(setpoint, sensorValue);
 PWM = (PidCorrection + (-output));
 CalibrazioneSensore();

 if (hold == 0) {
 digitalWrite(DIR, direzione); 
 Timer1.pwm(STEPpin, DUTY, PWM);
 ////////////  debug
 if (debug == 1) {
 Serial.println(PWM);
 Serial.println(sensorValue);
 }
 }

 /////////////// to be rewritten
 /////////////// what should I do if motor has reached target
 if (hold == 0 && sensorValue <= setpoint + diff_error && check == 0) {
 Timer1.stop();
 hold = 1;
 I2cSendOK();
 check = 1;
 }

 if (hold == 1) {
 holdPosition();
 }
 ////////////  debug
 if (debug == 1) {
    Serial.println(ads.readADC_SingleEnded(IngressoADC));
 }



}

////  receive new setpoint
void receiveEvent(int rcve) {
 ////// receive new setpoint via I2c
 check = 0;
 int iRXVal;
 if (Wire.available() >= 2)                  // Make sure there are two bytes.
 {

 for (int i = 0; i < 2; i++)             // Receive and rebuild the integer
 iRXVal += Wire.read() << (i * 8);   

 }

 int x = iRXVal; // receive byte as an integer

 ////////////  debug
 if (debug == 1) {
 Serial.println("ricevo via I2c");
 Serial.println(iRXVal);                 // Print the result.
 Serial.println(x); // print the integer
 }
 if (x > -32768) {
 ///////////////  what to do if I received a new value via I2c

 // set direction of rotation
 if (x > 0) {
 direzione = 1;
 hold = 0;
 setpoint = map(x, 0, 3600, sensorMIN, sensorMAX);  // map angle values in sensor values (should be viceversa??)
 }
 if (x < 0) {
 direzione = 0;
 hold = 0;
 setpoint = map(x, 0, 3600, sensorMIN, sensorMAX);  // map angle values in sensor values (should be viceversa??)
 }
 if (x == 0) {
 direzione = 0;
 hold = 1;
 }
 
 digitalWrite(ENA, LOW); //enable motor
 }
 else
 {
 //////////////  if I receive this -32768 via I2c
 digitalWrite(ENA, HIGH); //disattiva motore
 }

}

//// send setpoint reached - movement completed
void I2cSendOK() {
 ////// send -555 / setpoint reached
 int x = -555;
 Wire.beginTransmission(1); // transmit to device #1 (master)
 Wire.write((byte*)& x, 2); // Transmit the 'int', one byte at a time.
 Wire.endTransmission(); // stop transmitting
 ////////////  debug
 if (debug == 1) {
 Serial.println("inviato ok (-555)");
 }
}

////  hold position like a servo
void holdPosition() {
 sensorValue = ads.readADC_SingleEnded(IngressoADC);
 if (sensorValue <= setpoint && abs(sensorValue - setpoint) >  diff_error) {  // to be modified
 //todo: set direction of rotation and functions for math around the 0 point
 

 //XXXXXXXXXXXXX

 //run motor
 digitalWrite(DIR, direzione);
 Timer1.pwm(STEPpin, DUTY, PWM);
 CalibrazioneSensore();  // keep veryfy if sensor has new range of values
 }
 Timer1.stop();  // stop pulses to motor
}

////  calibrate minimum and maximum sensor values
void CalibrazioneSensore () {
 sensorValue = ads.readADC_SingleEnded(IngressoADC);
 if (sensorValue > sensorMAX) {
 sensorMAX = sensorValue;
 }
 if (sensorValue < sensorMIN) {
 sensorMIN = sensorValue;
 }
}

DKWatson:
No, because 0 - 1 = 65535. 65535%5 = 0. (0-2)%5 will output 4.

I don't get the "."....

Here is a drawing of the machine. I am designing it with solidworks "evaluation edition" (still learning..)

tomy983:
I don't get the "."....

Heheh! I'm sorry :slight_smile:

Here for my slow brain...

No, because
0 - 1 = 65535
65535%5 = 0
(0-2)%5 will output 4

DKWatson:
No, because 0 - 1 = 65535. 65535%5 = 0. (0-2)%5 will output 4.

it does not look like it's working...
Here is the code I am using to test:

The last bit seems to work. is a piece of code I found on this forum.

void setup() {
  Serial.begin(250000);

}

void loop() {
  uint16_t x = (0 - 1) % 5;
  Serial.print("x = (0 - 1) % 5  ");
  Serial.println(x); /// outputs 65535

  uint16_t y = (0-2)%5;
  Serial.print("(0-2)%5  ");
  Serial.println(y);  /// outputs 65534
  

  int a = (0-2);
  int mod =5;
  uint16_t val = (a<0 ? ((a+1)%mod)+mod-1 : a%mod);
  Serial.print("(a<0 ? ((a+1)%mod)+mod-1 : a%mod) ");
  Serial.println(val);
  Serial.end();

}

tomy983:
it does not look like it's working...

I think it does.

uint16_t baseVal = 0;

void setup() {
  Serial.begin(250000);
  Serial.print(F("baseVal = "));
  Serial.println(baseVal);
  Serial.print(F("baseVal - 1 = "));
  Serial.println(baseVal - 1);
  Serial.print(F("baseVal - 2 = "));
  Serial.println(baseVal - 2);
  Serial.print(F("(baseVal - 1) % 5 = "));
  Serial.println((baseVal - 1) % 5);
  Serial.print(F("(baseVal - 2) % 5 = "));
  Serial.println((baseVal - 2) % 5);
}
void loop() {}
baseVal = 0
baseVal - 1 = 65535
baseVal - 2 = 65534
(baseVal - 1) % 5 = 0
(baseVal - 2) % 5 = 4

Whandall:
I think it does.

Indeed it does. Please, what is different from what I wrote to what you wrote??

Your code uses a signed 0, my code an unsigned zero.

void setup() {
  Serial.begin(250000);

  uint16_t x = (0u - 1) % 5;
  Serial.print("x = (0u - 1) % 5  ");
  Serial.println(x);

  uint16_t X = (0 - 1) % 5;
  Serial.print("X = (0 - 1) % 5  ");
  Serial.println(X);

  uint16_t y = (0u - 2) % 5;
  Serial.print("y = (0u - 2) % 5  ");
  Serial.println(y);

  uint16_t Y = (0 - 2) % 5;
  Serial.print("Y = (0 - 2) % 5  ");
  Serial.println(Y);
}
void loop() {}
x = (0u - 1) % 5  0
X = (0 - 1) % 5  65535
y = (0u - 2) % 5  4
Y = (0 - 2) % 5  65534