Five days, five tests to push this project along. All focusing on getting a motor to move with input from a sensor.
Five days, five tests to push this project along. All focusing on getting a motor to move with input from a sensor.
Smoothing
Using the example code for smoothing form the Arduino IDE, I wanted to see how averaging worked using a simple analogRead()
with a rotatory potentiometer. This was pretty straight forward and worked as anticipated.
/* Smoothing Reads repeatedly from an analog input, calculating a running average and printing it to the computer. Keeps ten readings in an array and continually averages them. The circuit: - analog sensor (potentiometer will do) attached to analog input 0 created 22 Apr 2007 by David A. Mellis <dam@mellis.org> modified 9 Apr 2012 by Tom Igoe This example code is in the public domain. http://www.arduino.cc/en/Tutorial/Smoothing */ // Define the number of samples to keep track of. The higher the number, the // more the readings will be smoothed, but the slower the output will respond to // the input. Using a constant rather than a normal variable lets us use this // value to determine the size of the readings array. const int numReadings = 10; int readings[numReadings]; // the readings from the analog input int readIndex = 0; // the index of the current reading int total = 0; // the running total int average = 0; // the average int inputPin = A0; void setup() { // initialize serial communication with computer: Serial.begin(9600); // initialize all the readings to 0: for (int thisReading = 0; thisReading < numReadings; thisReading++) { readings[thisReading] = 0; } } void loop() { // subtract the last reading: total = total - readings[readIndex]; // read from the sensor: readings[readIndex] = analogRead(inputPin); // add the reading to the total: total = total + readings[readIndex]; // advance to the next position in the array: readIndex = readIndex + 1; // if we're at the end of the array... if (readIndex >= numReadings) { // ...wrap around to the beginning: readIndex = 0; } // calculate the average: average = total / numReadings; // send it to the computer as ASCII digits Serial.println(average); delay(1); // delay in between reads for stability }
Smoothing-VL53L0X
Since one of the variables I will be using in the final installation is proximity or distance, I decided to use the Adafruit VL53L0X breakout board to test the smoothing code. I played with the const int numReadings
to see how this changed the output of the averaging. As expected the more values held in the array the slower the average changed, this will affect how fast the motor is told to change position.
#include "Adafruit_VL53L0X.h" Adafruit_VL53L0X lox = Adafruit_VL53L0X(); const int numReadings = 50; int readings[numReadings]; // the readings from the analog input int readIndex = 0; // the index of the current reading int total = 0; // the running total int average = 0; // the average int distance; //int inputPin = A4; void setup() { // initialize serial communication with computer: Serial.begin(115200); // initialize all the readings to 0: for (int thisReading = 0; thisReading < numReadings; thisReading++) { readings[thisReading] = 0; } // wait until serial port opens for native USB devices while (! Serial) { delay(1); } Serial.println("Adafruit VL53L0X test"); if (!lox.begin()) { Serial.println(F("Failed to boot VL53L0X")); while(1); } // power Serial.println(F("VL53L0X API Simple Ranging example\n\n")); } void loop() { VL53L0X_RangingMeasurementData_t measure; distance = measure.RangeMilliMeter; // Serial.print("Reading a measurement... ") lox.rangingTest(&measure, false); // pass in 'true' to get debug data printout! // Serial.print("Distance (mm): ");Serial.println(distance); delay(100); // subtract the last reading: total = total - readings[readIndex]; // read from the sensor: // readings[readIndex] = analogRead(inputPin); readings[readIndex] = distance; // add the reading to the total: total = total + readings[readIndex]; // advance to the next position in the array: readIndex = readIndex + 1; // if we're at the end of the array... if (readIndex >= numReadings) { // ...wrap around to the beginning: readIndex = 0; } // calculate the average: average = total / numReadings; // send it to the computer as ASCII digits Serial.print("D");Serial.print(distance);Serial.print("A");Serial.println(average); // Serial.print("Average");Serial.println(average); delay(10); // delay in between reads for stability }
Pololu USB Control
Pololu has a very nice application for controlling the motor driver over USB. In this case I choose the Tic T834 which unfortunately was too low power to drive the NEMA 23 motors of the X-Carve I was testing with. Luckily I had a much smaller NEMA 8 that works great with this driver.
Pololu Tic T834 Position Control I2C
Next I wanted to droive the motor position using the library from Pololu and fixed position movement, in this case 200 steps clockwise and then -200 steps counter clockwise.
#include <Tic.h> TicI2C tic; void setup() { // Set up I2C. Wire.begin(); // Give the Tic some time to start up. delay(20); // Set the Tic's current position to 0, so that when we command // it to move later, it will move a predictable amount. tic.haltAndSetPosition(0); // Tells the Tic that it is OK to start driving the motor. The // Tic's safe-start feature helps avoid unexpected, accidental // movement of the motor: if an error happens, the Tic will not // drive the motor again until it receives the Exit Safe Start // command. The safe-start feature can be disbled in the Tic // Control Center. tic.exitSafeStart(); } // Sends a "Reset command timeout" command to the Tic. We must // call this at least once per second, or else a command timeout // error will happen. The Tic's default command timeout period // is 1000 ms, but it can be changed or disabled in the Tic // Control Center. void resetCommandTimeout() { tic.resetCommandTimeout(); } // Delays for the specified number of milliseconds while // resetting the Tic's command timeout so that its movement does // not get interrupted by errors. void delayWhileResettingCommandTimeout(uint32_t ms) { uint32_t start = millis(); do { resetCommandTimeout(); } while ((uint32_t)(millis() - start) <= ms); } // Polls the Tic, waiting for it to reach the specified target // position. Note that if the Tic detects an error, the Tic will // probably go into safe-start mode and never reach its target // position, so this function will loop infinitely. If that // happens, you will need to reset your Arduino. void waitForPosition(int32_t targetPosition) { do { resetCommandTimeout(); } while (tic.getCurrentPosition() != targetPosition); } void loop() { // Tell the Tic to move to position 100, and wait until it gets // there. tic.setTargetPosition(100); waitForPosition(100); // Tell the Tic to move to position -100, and delay for 3000 ms // to give it time to get there. tic.setTargetPosition(-100); delayWhileResettingCommandTimeout(3000); }
Tic T834 using the VL53L0X for position control
Finally this is still a work in progress as demonstrated by the video below, I do not currently have this working. However, it is only a matter of time to figure it out.
#include "Adafruit_VL53L0X.h" Adafruit_VL53L0X lox = Adafruit_VL53L0X(); unsigned long startMillis; unsigned long currentMillis; const unsigned long period = 4000; const int numReadings = 10; int readings[numReadings]; // the readings from the analog input int readIndex = 0; // the index of the current reading int total = 0; // the running total int average = 0; // the average int distance = 0; //int inputPin = A4; void setup() { // initialize serial communication with computer: Serial.begin(115200); // initialize all the readings to 0: for (int thisReading = 0; thisReading < numReadings; thisReading++) { readings[thisReading] = 0; } // wait until serial port opens for native USB devices while (! Serial) { delay(1); } Serial.println("Adafruit VL53L0X test"); if (!lox.begin()) { Serial.println(F("Failed to boot VL53L0X")); while (1); } // power Serial.println(F("VL53L0X API Simple Ranging example\n\n")); startMillis = millis(); //initial start time } void loop() { currentMillis = millis(); VL53L0X_RangingMeasurementData_t measure; distance = measure.RangeMilliMeter; lox.rangingTest(&measure, false); // pass in 'true' to get debug data printout! delay(100); // subtract the last reading: total = total - readings[readIndex]; // read from the sensor: // readings[readIndex] = analogRead(inputPin); readings[readIndex] = distance; // add the reading to the total: total = total + readings[readIndex]; // advance to the next position in the array: readIndex = readIndex + 1; // if we're at the end of the array... if (readIndex >= numReadings) { // ...wrap around to the beginning: readIndex = 0; } // calculate the average: average = total / numReadings; int steps = map(average, 0, 9000, -400, 400); // send it to the computer as ASCII digits Serial.print("Distance"); Serial.println(distance); Serial.print("Average"); Serial.println(average); if (currentMillis - startMillis <= period) { analogWrite(A0, steps); // delay(5000); // delay in between reads for stability } startMillis = currentMillis; }
A few things to note, I was using an Arduino Uno R3 to start with, this worked well for driving the motor as it is a 5V native board, however, the Uno had some issues when interfacing with the Adafruit VL53L0X. Adafruit claims comatibility with the Uno and 5V but for some reason I wopuld get negetive numbers in both the distance reporting for the sensor and the averaging. I’m not sure why this was the case but changing the board to an Arduino Nano 33 IoT made everything function as expected.