C++ code for meArm arduino robot doesn't work - arduino

I'm having some issues with this code for meArm arduino robot.
It should ask the user to input the values for the angles of the servo motors and rotate them, however it only does so the first 2 times and then stops working.
Is there any error in the code?
#include <Servo.h>
int angle1;
int angle2;
int angle3;
Servo s1;
Servo s2;
Servo s3;
Servo s4;
const int BUFFER_SIZE = 100;
char buf[BUFFER_SIZE];
void setup() {
Serial.begin(9600);
s1.attach(10);
s2.attach(9);
s3.attach(8);
s1.write(90);
s2.write(0);
s3.write(0);
}
void loop() {
Serial.println("Set the angle for s1 servo");
while (Serial.available() == 0) {};
angle1 = Serial.readBytesUntil('\n', buf, BUFFER_SIZE);
Serial.println("Set the angle for s2 servo");
while (Serial.available() == 0) {};
angle2 = Serial.readBytesUntil('\n', buf, BUFFER_SIZE);
Serial.println("Set the angle for s3 servo");
while (Serial.available() == 0) {};
angle3 = Serial.readBytesUntil('\n', buf, BUFFER_SIZE);
ForwardKinematics(angle1, angle2, angle3);
}
/**
* Rotates the servo motors by the given angles.
*
* #param angle1 The angle by which the servo s1 is rotated.
* #param angle2 The angle by which the servo s2 is rotated.
* #param angle3 The angle by which the servo s3 is rotated.
*/
void ForwardKinematics(int angle1, int angle2, int angle3) {
s1.write(angle1);
s2.write(angle2);
s3.write(angle3);
delay(500);
}

Your biggest problem is,
Serial.readBytesUntil('\n', buf, BUFFER_SIZE);
will only return the number of characters read into the buffer, not the data. Not sure what you mean by it works the first two times. Do you mean it loops through 2 times, or does it just get data for the first two servos and not the third one. Anyways, below is a working code tested on a UNO.
#include <Servo.h>
int angle1;
int angle2;
int angle3;
Servo s1;
Servo s2;
Servo s3;
Servo s4;
String data = "";
const int BUFFER_SIZE = 100;
char buf[BUFFER_SIZE];
void setup() {
Serial.begin(9600);
s1.attach(10);
s2.attach(9);
s3.attach(8);
s1.write(90);
s2.write(0);
s3.write(0);
}
void loop() {
angle1 = 0;
angle2 = 0;
angle3 = 0;
Serial.println("Set the angle for s1 servo");
while (Serial.available() == 0) {};
int rlen = Serial.readBytesUntil('\n', buf, BUFFER_SIZE);
for(int i = 0; i < rlen; i++){
data += buf[i];
}
angle1 = data.toInt();
data = "";
Serial.println("Set the angle for s2 servo");
while (Serial.available() == 0) {};
rlen = Serial.readBytesUntil('\n', buf, BUFFER_SIZE);
for(int i = 0; i < rlen; i++){
data += buf[i];
}
angle2 = data.toInt();
data = "";
Serial.println("Set the angle for s3 servo");
while (Serial.available() == 0) {};
rlen = Serial.readBytesUntil('\n', buf, BUFFER_SIZE);
for(int i = 0; i < rlen; i++){
data += buf[i];
}
angle3 = data.toInt();
data = "";
ForwardKinematics(angle1, angle2, angle3);
}
/**
* Rotates the servo motors by the given angles.
*
* #param angle1 The angle by which the servo s1 is rotated.
* #param angle2 The angle by which the servo s2 is rotated.
* #param angle3 The angle by which the servo s3 is rotated.
*/
void ForwardKinematics(int angle1, int angle2, int angle3) {
s1.write(angle1);
s2.write(angle2);
s3.write(angle3);
delay(500);
}

Related

Question on Two servo project activated by timer and water level sensor

I am using a arduino uno in an attempt to power two servos. Servo A should open for a couple seconds every 24 hours OR if button A is pressed. Servo B should open for a couple seconds if the water level sensor reads below 300 OR if button B is pressed. The code below works fine when I only include one servo, but adding the code for servo B ruins everything.
#include <Servo.h>
Servo myservoA;
Servo myservoB;
const int BUTTONA_PIN = 8;
const int BUTTONB_PIN = 6;
const int SERVOA_PIN = 9;
const int SERVOB_PIN = 7;
unsigned long dayTimer_ms = 0;
unsigned long autoOpenDelay_ms = 86400000;
int angle = 0;
int waterSensor = A0;
int waterLevel = 0;
void setup(){
myservoA.attach(SERVOA_PIN);
myservoB.attach(SERVOB_PIN);
pinMode(BUTTONA_PIN, INPUT_PULLUP);
pinMode(BUTTONB_PIN, INPUT_PULLUP);
myservoA.write(0);
myservoB.write(0);
}
void loop() {
if(millis() - dayTimer_ms > autoOpenDelay_ms)
{
dayTimer_ms = millis();
myservoA.write(180); //(open?)
delay(8000);
myservoA.write(0);
}
if(millis()<dayTimer_ms)//overflow handling (in case this runs for more than 50 days straight)
{
dayTimer_ms = millis();
}
if (!digitalRead(BUTTONA_PIN) && angle != 180)
{
angle = 180;
myservoA.write(angle);
}
if (digitalRead(BUTTONA_PIN) && angle != 0)
{
angle = 0;
myservoA.write(angle);
}
if (!digitalRead(BUTTONB_PIN) && angle != 180)
{
angle = 180;
myservoB.write(angle);
}
if (digitalRead(BUTTONB_PIN) && angle != 0)
{
angle = 0;
myservoB.write(angle);
}
int waterLevel = analogRead(waterSensor);
if (waterLevel <= 300){
myservoB.write(180);
delay(8000);
myservoB.write(0);
}
}
You are actually super close!
You just need to make a second variable to track angleA separately from angleB. So you should initialize another variable at the top "angleB" to zero and then replace "angle" with this new variable everywhere below line 52
#include <Servo.h>
Servo myservoA;
Servo myservoB;
const int BUTTONA_PIN = 8;
const int BUTTONB_PIN = 6;
const int SERVOA_PIN = 9;
const int SERVOB_PIN = 7;
unsigned long dayTimer_ms = 0;
unsigned long autoOpenDelay_ms = 86400000;
int angleA = 0;
int angleB = 0;
int waterSensor = A0;
int waterLevel = 0;
void setup(){
myservoA.attach(SERVOA_PIN);
myservoB.attach(SERVOB_PIN);
pinMode(BUTTONA_PIN, INPUT_PULLUP);
pinMode(BUTTONB_PIN, INPUT_PULLUP);
myservoA.write(0);
myservoB.write(0);
}
void loop() {
if(millis() - dayTimer_ms > autoOpenDelay_ms)
{
dayTimer_ms = millis();
myservoA.write(180); //(open?)
delay(8000);
myservoA.write(0);
angleA = 0;
}
if(millis()<dayTimer_ms)//overflow handling (in case this runs for more than 50 days straight)
{
dayTimer_ms = millis();
}
if (!digitalRead(BUTTONA_PIN) && angleA != 180)
{
angleA = 180;
myservoA.write(angleA);
}
if (digitalRead(BUTTONA_PIN) && angleA != 0)
{
angleA = 0;
myservoA.write(angleA);
}
if (!digitalRead(BUTTONB_PIN) && angleB != 180)
{
angleB = 180;
myservoB.write(angleB);
}
if (digitalRead(BUTTONB_PIN) && angleB != 0)
{
angleB = 0;
myservoB.write(angleB);
}
int waterLevel = analogRead(waterSensor);
if (waterLevel > 300 && angleB != 0)
{
myservoB.write(0);
angleB = 0;
}
else if (waterLevel < 200 && angleB != 180){
myservoB.write(180);
angleB = 180;
}
}
the bottom two levels may need to be adjusted (200 and 300). Basically this creates a slight de-bounce. By separating the levels at which the water turns on and turns off, it keeps the system from jittering right at the limit (constantly turning on and off). That being said, depending on the accuracy of your sensor and how important precise level control is in your application, you may want to tighten or move these values around a bit.

MPU6050 FIFO overflow and freezing problems

Im using the sensor MPU6050 to control movements of my robotic arm. The codes work fine when it is a standalone program but i keep encountering 'FIFO overflow' when the codes are complied into the main program. This is the code that i am using.
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
#include "I2Cdev.h"
#include "MPU6050_6Axis_MotionApps20.h"
#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
#include "Wire.h"
#endif
MPU6050 mpu;
RF24 radio(9, 8); // CE, CSN
const byte address[6] = "00001";
const int AccReadings = 10;
//Wrist Roll
int DataX[AccReadings];
int WRIndex = 0;
int WRtotal = 0;
int WRaverage = 0;
//Wrist Pitch
int DataY[AccReadings];
int WPIndex = 0;
int WPtotal = 0;
int WPaverage = 0;
//Shoulder Lift
int DataY2[AccReadings];
int SLIndex = 0;
int SLtotal = 0;
int SLaverage = 0;
//Elbow Lift
int ELaverage = 0;
//Arm Rotation
int ARaverage = 0;
float correct;
#define OUTPUT_READABLE_YAWPITCHROLL
#define INTERRUPT_PIN 2
bool blinkState = false;
// MPU control/status vars
bool dmpReady = false; // set true if DMP init was successful
uint8_t mpuIntStatus; // holds actual interrupt status byte from MPU
uint8_t devStatus; // return status after each device operation (0 = success, !0 = error)
uint16_t packetSize; // expected DMP packet size (default is 42 bytes)
uint16_t fifoCount; // count of all bytes currently in FIFO
uint8_t fifoBuffer[64]; // FIFO storage buffer
// orientation/motion vars
Quaternion q; // [w, x, y, z] quaternion container
VectorInt16 aa; // [x, y, z] accel sensor measurements
VectorInt16 aaReal; // [x, y, z] gravity-free accel sensor measurements
VectorInt16 aaWorld; // [x, y, z] world-frame accel sensor measurements
VectorFloat gravity; // [x, y, z] gravity vector
float euler[3]; // [psi, theta, phi] Euler angle container
float ypr[3]; // [yaw, pitch, roll] yaw/pitch/roll container and gravity vector
struct Sensor_Data
{
int WristRoll;
int WristPitch;
int ShoulderLift;
int ElbowLift;
int ArmRotation;
};
Sensor_Data data;
//Interrupt Detection
volatile bool mpuInterrupt = false;
void dmpDataReady()
{
mpuInterrupt = true;
}
void setup()
{
radio.begin();
radio.openWritingPipe(address);
radio.setPALevel(RF24_PA_MIN);
radio.stopListening();
//Zero-fill Arrays
for (int i = 0; i < AccReadings; i++)
{
DataX[i] = 0;
}
for (int j = 0; j < AccReadings; j++)
{
DataY[j] = 0;
}
for (int k = 0; k < AccReadings; k++)
{
DataY2[k] = 0;
}
#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
Wire.begin();
Wire.setClock(400000);
#elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE
Fastwire::setup(400, true);
#endif
Serial.begin(115200);
while (!Serial);
mpu.initialize();
pinMode(INTERRUPT_PIN, INPUT);
devStatus = mpu.dmpInitialize();
mpu.setXGyroOffset(49);
mpu.setYGyroOffset(-18);
mpu.setZGyroOffset(9);
mpu.setZAccelOffset(4427);
if (devStatus == 0)
{
mpu.setDMPEnabled(true);
attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), dmpDataReady, RISING);
mpuIntStatus = mpu.getIntStatus();
dmpReady = true;
packetSize = mpu.dmpGetFIFOPacketSize();
}
else
{
// ERROR!
// 1 = initial memory load failed
// 2 = DMP configuration updates failed
// (if it's going to break, usually the code will be 1)
// Serial.print(F("DMP Initialization failed (code "));
//Serial.print(devStatus);
//Serial.println(F(")"));
}
}
void loop()
{
/*smoothWR();
movementWR();
smoothWP();
movementWP();
smoothSL();
movementSL();*/
ElbowMovement();
radio.write(&data, sizeof(Sensor_Data));
}
void smoothWR()
{
WRtotal = WRtotal - DataX[WRIndex];
DataX[WRIndex] = analogRead(A0);
WRtotal = WRtotal + DataX[WRIndex];
WRIndex = WRIndex + 1;
if (WRIndex >= AccReadings)
{
WRIndex = 0;
}
WRaverage = WRtotal / AccReadings;
//Serial.println(WRaverage);
}
void movementWR()
{
WRaverage = map(WRaverage, 278, 419, 0, 180);
data.WristRoll = constrain(WRaverage, 0, 180);
//Serial.println(data.WristRoll);
}
void smoothWP()
{
WPtotal = WPtotal - DataY[WPIndex];
DataY[WPIndex] = analogRead(A1);
WPtotal = WPtotal + DataY[WPIndex];
WPIndex = WPIndex + 1;
if (WPIndex >= AccReadings)
{
WPIndex = 0;
}
WPaverage = WPtotal / AccReadings;
//Serial.println(WPaverage);
}
void movementWP()
{
WPaverage = map(WPaverage, 280, 421, 0 , 135);
data.WristPitch = constrain(WPaverage, 0, 135);
//Serial.println(data.WristPitch);
}
void smoothSL()
{
SLtotal = SLtotal - DataY2[SLIndex];
DataY2[SLIndex] = analogRead(A2);
SLtotal = SLtotal + DataY2[SLIndex];
SLIndex = SLIndex + 1;
if (SLIndex >= AccReadings)
{
SLIndex = 0;
}
SLaverage = SLtotal / AccReadings;
//Serial.println(SLaverage);
}
void movementSL()
{
SLaverage = map(SLaverage, 410, 270, 0 , 180);
data.ShoulderLift = constrain(SLaverage, 35, 180);
//Serial.println(data.ShoulderLift);
}
void ElbowMovement()
{
// if programming failed, don't try to do anything
if (!dmpReady) return;
// wait for MPU interrupt or extra packet(s) available
while (!mpuInterrupt && fifoCount < packetSize)
{
if (mpuInterrupt && fifoCount < packetSize)
{
// try to get out of the infinite loop
fifoCount = mpu.getFIFOCount();
}
}
// reset interrupt flag and get INT_STATUS byte
mpuInterrupt = false;
mpuIntStatus = mpu.getIntStatus();
// get current FIFO count
fifoCount = mpu.getFIFOCount();
// check for overflow (this should never happen unless our code is too inefficient)
if ((mpuIntStatus & _BV(MPU6050_INTERRUPT_FIFO_OFLOW_BIT)) || fifoCount >= 1024)
{
// reset so we can continue cleanly
mpu.resetFIFO();
fifoCount = mpu.getFIFOCount();
Serial.println(F("FIFO overflow!"));
// otherwise, check for DMP data ready interrupt (this should happen frequently)
}
else if (mpuIntStatus & _BV(MPU6050_INTERRUPT_DMP_INT_BIT))
{
// wait for correct available data length, should be a VERY short wait
while (fifoCount < packetSize) fifoCount = mpu.getFIFOCount();
// read a packet from FIFO
mpu.getFIFOBytes(fifoBuffer, packetSize);
// track FIFO count here in case there is > 1 packet available
// (this lets us immediately read more without waiting for an interrupt)
fifoCount -= packetSize;
// Get Yaw, Pitch and Roll values
#ifdef OUTPUT_READABLE_YAWPITCHROLL
mpu.dmpGetQuaternion(&q, fifoBuffer);
mpu.dmpGetGravity(&gravity, &q);
mpu.dmpGetYawPitchRoll(ypr, &q, &gravity);
// Yaw, Pitch, Roll values - Radians to degrees
ypr[0] = ypr[0] * 180 / M_PI;
ypr[1] = ypr[1] * 180 / M_PI;
ypr[2] = ypr[2] * 180 / M_PI;
// Skip 300 readings (self-calibration process)
if (int l = 0; l <= 300) {
correct = ypr[0]; // Yaw starts at random value, so we capture last value after 300 readings
l++;
}
// After 300 readings
else {
ypr[0] = ypr[0] - correct; // Set the Yaw to 0 deg - subtract the last random Yaw value from the currrent value to make the Yaw 0 degrees
// Map the values of the MPU6050 sensor from -90 to 90 to values suatable for the servo control from 0 to 180
ELaverage = map(ypr[0], -90, 90, 0, 180);
data.ElbowLift = constrain(ELaverage, 30, 110);
ARaverage = map(ypr[1], -90, 90, 0, 180);
data.ArmRotation = constrain(ARaverage, 0, 180);
//Serial.println(data.ElbowLift);
Serial.println(ypr[1]);
}
#endif
}
}
Is there any ways to get rid of the FIFO overflow? Also When i tried to used Jeff Rowberg's example codes MPU6050_DMP6 the program will freeze after a few seconds. Is there any solution to that? These are the example codes that i am using.
#include "I2Cdev.h"
#include "MPU6050_6Axis_MotionApps20.h"
#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
#include "Wire.h"
#endif
MPU6050 mpu;
float correct;
int j = 0;
#define OUTPUT_READABLE_YAWPITCHROLL
#define INTERRUPT_PIN 2
bool blinkState = false;
// MPU control/status vars
bool dmpReady = false; // set true if DMP init was successful
uint8_t mpuIntStatus; // holds actual interrupt status byte from MPU
uint8_t devStatus; // return status after each device operation (0 = success, !0 = error)
uint16_t packetSize; // expected DMP packet size (default is 42 bytes)
uint16_t fifoCount; // count of all bytes currently in FIFO
uint8_t fifoBuffer[64]; // FIFO storage buffer
// orientation/motion vars
Quaternion q; // [w, x, y, z] quaternion container
VectorInt16 aa; // [x, y, z] accel sensor measurements
VectorInt16 aaReal; // [x, y, z] gravity-free accel sensor measurements
VectorInt16 aaWorld; // [x, y, z] world-frame accel sensor measurements
VectorFloat gravity; // [x, y, z] gravity vector
float euler[3]; // [psi, theta, phi] Euler angle container
float ypr[3]; // [yaw, pitch, roll] yaw/pitch/roll container and gravity vector
//Interrupt Detection
volatile bool mpuInterrupt = false; // indicates whether MPU interrupt pin has gone high
void dmpDataReady() {
mpuInterrupt = true;
}
void setup()
{
#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
Wire.begin();
Wire.setClock(400000);
#elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE
Fastwire::setup(400, true);
#endif
Serial.begin(38400);
while (!Serial);
mpu.initialize();
pinMode(INTERRUPT_PIN, INPUT);
devStatus = mpu.dmpInitialize();
mpu.setXGyroOffset(17);
mpu.setYGyroOffset(-69);
mpu.setZGyroOffset(27);
mpu.setZAccelOffset(1551);
if (devStatus == 0)
{
mpu.setDMPEnabled(true);
attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), dmpDataReady, RISING);
mpuIntStatus = mpu.getIntStatus();
dmpReady = true;
packetSize = mpu.dmpGetFIFOPacketSize();
}
else
{
// ERROR!
// 1 = initial memory load failed
// 2 = DMP configuration updates failed
// (if it's going to break, usually the code will be 1)
// Serial.print(F("DMP Initialization failed (code "));
//Serial.print(devStatus);
//Serial.println(F(")"));
}
}
void loop()
{
if (!dmpReady) return;
if (mpu.dmpGetCurrentFIFOPacket(fifoBuffer))
{
#ifdef OUTPUT_READABLE_YAWPITCHROLL
// display Euler angles in degrees
mpu.dmpGetQuaternion(&q, fifoBuffer);
mpu.dmpGetGravity(&gravity, &q);
mpu.dmpGetYawPitchRoll(ypr, &q, &gravity);
if (j <= 300)
{
correct = ypr[0]; // Yaw starts at random value, so we capture last value after 300 readings
j++;
}
else
{
ypr[0] = ypr[0] - correct;
Serial.print("ypr\t");
Serial.print(ypr[0] * 180/M_PI);
Serial.print("\t");
Serial.print(ypr[1] * 180/M_PI);
Serial.print("\t");
Serial.println(ypr[2] * 180/M_PI);
}
#endif
}
}
You're using DMP (Digital Motion Processor) with mean calculation running on the MPU itself, this gives more precise and less CPU consumption but you need to update the FIFO frequently or the track goes wrong.
Encountering 'FIFO overflow' means your loop code was too slow, you should increase the speed of another task in your loop code. Or just use other code that not use the DMP.

Divided and duplicated frame capture output for ADNS 9800 Arduino

I am trying to interface an ADNS 9800 mouse chip which I took from "Sharkoon SHARK ZONE M50" . The original PCB is still in place.
I am trying to obtain a framecapture, which should be 30 x 30 pixels. I have connected the ADNS 9800 with SPI to an Arduino UNO Rev 3 (i.e. 5V operating voltage). I.e. MISO, MOSI, SCLK, DGND, AGND, NCS. I did not connect any voltage, since I concluded from previous attempts that that did not yield a good frame capture.
The current problem is that I get a frame capture that is divided in 3 parts: square top left (with a good image of the surroundings), square bottom left (which is a duplicate of top left) and a rectangle on the right half of the screen of monotone grey colour (which does change depending on light conditions). See image. I want the full screen to be one image, not the divided mess it is now. Therefore, it may be a question of the resolution that is used, it may be that it is 15x15 instead of 30x30. However I do not know where this is determined/set.
Also, I find it strange that no input voltage seems to be needed to obtain an image from the camera.
See attachments for frame capture and code (arduino + processing).
Frame output
Arduino code
#include
#include
// Registers
#define REG_Product_ID 0x00
#define REG_Revision_ID 0x01
#define REG_Motion 0x02
#define REG_Delta_X_L 0x03
#define REG_Delta_X_H 0x04
#define REG_Delta_Y_L 0x05
#define REG_Delta_Y_H 0x06
#define REG_SQUAL 0x07
#define REG_Pixel_Sum 0x08
#define REG_Maximum_Pixel 0x09
#define REG_Minimum_Pixel 0x0a
#define REG_Shutter_Lower 0x0b
#define REG_Shutter_Upper 0x0c
#define REG_Frame_Period_Lower 0x0d
#define REG_Frame_Period_Upper 0x0e
#define REG_Configuration_I 0x0f
#define REG_Configuration_II 0x10
#define REG_Frame_Capture 0x12
#define REG_SROM_Enable 0x13
#define REG_Run_Downshift 0x14
#define REG_Rest1_Rate 0x15
#define REG_Rest1_Downshift 0x16
#define REG_Rest2_Rate 0x17
#define REG_Rest2_Downshift 0x18
#define REG_Rest3_Rate 0x19
#define REG_Frame_Period_Max_Bound_Lower 0x1a
#define REG_Frame_Period_Max_Bound_Upper 0x1b
#define REG_Frame_Period_Min_Bound_Lower 0x1c
#define REG_Frame_Period_Min_Bound_Upper 0x1d
#define REG_Shutter_Max_Bound_Lower 0x1e
#define REG_Shutter_Max_Bound_Upper 0x1f
#define REG_LASER_CTRL0 0x20
#define REG_Observation 0x24
#define REG_Data_Out_Lower 0x25
#define REG_Data_Out_Upper 0x26
#define REG_SROM_ID 0x2a
#define REG_Lift_Detection_Thr 0x2e
#define REG_Configuration_V 0x2f
#define REG_Configuration_IV 0x39
#define REG_Power_Up_Reset 0x3a
#define REG_Shutdown 0x3b
#define REG_Inverse_Product_ID 0x3f
#define REG_Snap_Angle 0x42
#define REG_Motion_Burst 0x50
#define REG_SROM_Load_Burst 0x62
#define REG_Pixel_Burst 0x64
byte initComplete=0;
byte testctr=0;
unsigned long currTime;
unsigned long timer;
volatile int xdat;
volatile int ydat;
volatile byte movementflag=0;
const int ncs = 10;
const int lsPin = 4;//ANALOG
const int linearActPin = 9;
extern const unsigned short firmware_length;
extern const unsigned char firmware_data[];
String parseChar = ".";
void setup() {
Serial.begin(115200);
//For first parse put LF and CR there
Serial.println("");
//pinMode(ls, INPUT);
//ADNS 9800 setup
pinMode (ncs, OUTPUT);
SPI.begin();
SPI.setDataMode(SPI_MODE3);
SPI.setBitOrder(MSBFIRST);
//Set clock to 2 MHz
SPI.setClockDivider(8);
performStartup();
dispRegisters();
delay(100);
//Pin modes
pinMode(linearActPin, OUTPUT);
Serial.print("Ready");
Serial.println(parseChar);
//Serial.println("Device is ready");
//FrameCapture();
}
/* DO NOT EDIT BELOW; NECESSARY FOR ADNS9800 */
void performStartup(void){
// reset the chip
adns_com_end(); // ensure that the serial port is reset
adns_com_begin(); // ensure that the serial port is reset
adns_com_end(); // ensure that the serial port is reset
adns_write_reg(REG_Power_Up_Reset, 0x5a); // force reset
delay(50); // wait for it to reboot
// read registers 0x02 to 0x06 (and discard the data)
adns_read_reg(REG_Delta_X_L);
adns_read_reg(REG_Delta_X_H);
adns_read_reg(REG_Delta_Y_L);
adns_read_reg(REG_Delta_Y_H);
// upload the firmware
adns_upload_firmware();
delay(10);
//enable laser(bit 0 = 0b), in normal mode (bits 3,2,1 = 000b)
// reading the actual value of the register is important because the real
// default value is different from what is said in the datasheet, and if you
// change the reserved bytes (like by writing 0x00...) it would not work.
byte laser_ctrl0 = adns_read_reg(REG_LASER_CTRL0);
adns_write_reg(REG_LASER_CTRL0, laser_ctrl0 & 0xf1 );
//0x08 = enable fixed framerate, leave rest standard
//0x10 = disable AGC, leave rest standard
adns_write_reg(REG_Configuration_II, 0x08);
//Set resolution; cpi = REG_value x50
//Min: 0x01 50 cpi
//Max: 0xA4 8200 cpi
adns_write_reg(REG_Configuration_I, 0xA4);
//Set fixed framerate: FR = clk_freq/REG_value = 2000 fps
adns_write_reg(REG_Frame_Period_Max_Bound_Lower, 0xa8);
adns_write_reg(REG_Frame_Period_Max_Bound_Upper, 0x61);
//Set shutter time
adns_write_reg(REG_Shutter_Max_Bound_Lower,0x00);
adns_write_reg(REG_Shutter_Max_Bound_Upper,0x08);
//adns_write_reg(REG_Snap_Angle, 0x80);
delay(1);
Serial.print("Initialized");
Serial.println(parseChar);
}
void adns_com_begin(){
digitalWrite(ncs, LOW);
}
void adns_com_end(){
digitalWrite(ncs, HIGH);
}
byte adns_read_reg(byte reg_addr){
adns_com_begin();
// send adress of the register, with MSBit = 0 to indicate it's a read
SPI.transfer(reg_addr & 0x7f );
delayMicroseconds(100); // tSRAD
// read data
byte data = SPI.transfer(0);
delayMicroseconds(1); // tSCLK-NCS for read operation is 120ns
adns_com_end();
delayMicroseconds(19); // tSRW/tSRR (=20us) minus tSCLK-NCS
return data;
}
void adns_write_reg(byte reg_addr, byte data){
adns_com_begin();
//send adress of the register, with MSBit = 1 to indicate it's a write
SPI.transfer(reg_addr | 0x80 );
//sent data
SPI.transfer(data);
delayMicroseconds(20); // tSCLK-NCS for write operation
adns_com_end();
delayMicroseconds(100); // tSWW/tSWR (=120us) minus tSCLK-NCS. Could be shortened, but is looks like a safe lower bound
}
void adns_upload_firmware(){
// send the firmware to the chip, cf p.18 of the datasheet
//Serial.println("Uploading firmware...");
// set the configuration_IV register in 3k firmware mode
adns_write_reg(REG_Configuration_IV, 0x02); // bit 1 = 1 for 3k mode, other bits are reserved
// write 0x1d in SROM_enable reg for initializing
delay(10);
adns_write_reg(REG_SROM_Enable, 0x1d);
// wait for more than one frame period
delay(10); // assume that the frame rate is as low as 100fps... even if it should never be that low
// write 0x18 to SROM_enable to start SROM download
adns_write_reg(REG_SROM_Enable, 0x18);
// write the SROM file (=firmware data)
adns_com_begin();
//write burst destination adress
//bitwise OR to ensure MSB is 1
SPI.transfer(REG_SROM_Load_Burst | 0x80);
delayMicroseconds(50);
// send all bytes of the firmware
unsigned char c;
for(int i = 0; i < firmware_length; i++){
c = (unsigned char)pgm_read_byte(firmware_data + i);
SPI.transfer(c);
delayMicroseconds(15);
}
adns_com_end();
}
void adns_frame_capture(){
//Send signal to start datacollection frame capture
Serial.print("Frame capture");
Serial.println(parseChar);
// reset the chip
adns_write_reg(REG_Power_Up_Reset, 0x5a); // force reset
delay(50); // wait for it to reboot
delay(10);
//Write bytes to Frame_Capture
adns_write_reg(REG_Frame_Capture, 0x93);
adns_write_reg(REG_Frame_Capture, 0xc5);
// wait for more than two frame periods
delay(25); // assume that the frame rate is as low as 100fps... even if it should never be that low
//Check for the first pixel bij reading bit zero of Motion register
//If it is 1, first pixel available
byte motion = adns_read_reg(REG_Motion);
adns_com_begin();
delayMicroseconds(120);//delay t-SRAD = 100 us
byte pixel_burst;
if (motion == 0x21){
//Reading pixel values from ADNS and storing them in Array
for(int i = 0; i < 900; i++){
pixel_burst = adns_read_reg(REG_Pixel_Burst);
//Serial.print(i);
//Serial.print(":");
Serial.print(String(pixel_burst));
Serial.println(parseChar);
delayMicroseconds(15);
}
//Finished transmitting data
Serial.print("Data transfer finished");
Serial.println(parseChar);
//Transfer surface quality value
Serial.print("SQUAL");
Serial.print(String(adns_read_reg(REG_SQUAL)));
Serial.println(parseChar);
}else {
Serial.print("Frame capture failed");
Serial.println(parseChar);
}
adns_com_end();
//Hardware reset and firmware restore required to return navigation
performStartup();
}
void dispRegisters(void){
int oreg[7] = {
0x00,0x3F,0x2A,0x02 };
char* oregname[] = {
"Product_ID","Inverse_Product_ID","SROM_Version","Motion" };
byte regres;
digitalWrite(ncs,LOW);
int rctr=0;
for(rctr=0; rctr<4; rctr++){
SPI.transfer(oreg[rctr]);
delay(1);
//Serial.println("---");
//Serial.println(oregname[rctr]);
//Serial.println(oreg[rctr],HEX);
regres = SPI.transfer(0);
//Serial.println(regres,BIN);
//Serial.println(regres,HEX);
delay(1);
}
digitalWrite(ncs,HIGH);
}
/*********************************************************
DO NOT EDIT ABOVE; NECESSARY FOR RUNNING ADNS9800
*********************************************************/
String data = String();
//Process variables
int run = 0;
int t = 0;
unsigned long t_ms, t_us;
int dt = 0;//1/f = [ms]
long int t_run = 0;//[ms]
unsigned long ms_start, us_start;
void loop() {
if (dt == -1 || t_run == -1){
Serial.print("Time constant error");
Serial.println(parseChar);
}else if (run == 1 && t<t_run){
measure();
Serial.print(data);
Serial.println("");
Serial.println(parseChar);
}else if(run == 1 && t>=t_run){
//Measurement finished
Serial.print("Measurement finished");
Serial.println(parseChar);
digitalWrite(linearActPin, LOW);
run = 0;
t = 0;
}
}
void serialEvent(){
String data_rx;
if (Serial.available() > 0){
//Parse serial data until '.'
data_rx = Serial.readStringUntil('.');
//Remove '.' from buffer
data_rx = data_rx.substring(0, data_rx.length());
//Serial.print(data_rx);
if (data_rx.equals("Run")){
run = 1;
ms_start = millis();
us_start = micros();
digitalWrite(linearActPin, HIGH);
//Read registers and discard data
byte XDataL = adns_read_reg(REG_Delta_X_L);
byte XDataH = adns_read_reg(REG_Delta_X_H);
byte YDataL = adns_read_reg(REG_Delta_Y_L);
byte YDataH = adns_read_reg(REG_Delta_Y_H);
}else if(data_rx.equals("Frame capture run")){
adns_frame_capture();
}else if(data_rx.equals("SQUAL")){
Serial.println(String(adns_read_reg(REG_SQUAL)));
}else if(data_rx.startsWith("dt")){
dt = data_rx.substring(2,data_rx.length()).toInt();
}else if(data_rx.startsWith("trun")){
t_run = data_rx.substring(4,data_rx.length()).toInt();
}
}
}
void measure(void){
/*READ dx, dy, ls
increment t with dt
return String "t,dx,dy,ls"*/
//Read optic flow from ADNS
byte XDataL = adns_read_reg(REG_Delta_X_L);
byte XDataH = adns_read_reg(REG_Delta_X_H);
byte YDataL = adns_read_reg(REG_Delta_Y_L);
byte YDataH = adns_read_reg(REG_Delta_Y_H);
int ls;
unsigned long us, ms;
xdat = int(XDataH<<8);
ydat = int(YDataH<<8);
xdat |=int(XDataL);
ydat |=int(YDataL);
//int between 0-1023, with 5V/1024 = 0.0049 V/unit
ls = analogRead(lsPin);
//Calculate time elapsed between measurements
ms = millis();
us = micros();
t_ms = ms-ms_start;
t_us = us-us_start;
t = t_ms;
//Convert datatypes to string objects and combine
//us can always be divided by 4, so accurate to a resolution of 4 us
String d1 = String(t_ms);
String d2 = String(t_us);
String d3 = String(xdat);
String d4 = String(ydat);
String d5 = String(ls);
data = d2+","+d3+","+d4+","+d5;
//Increment time
delay(dt);
}
Processing code
/* BEP experiment
Communicates with arduino to conduct experiment
Receives and stores data
/
/ DATA PROTOCOL
data_rx
R start measuring
S do screendump
D device is ready
F measurement finished
/
import processing.serial.;
import controlP5.*;
//Serial COMM
Serial arduino;
String data_rx, data_tx;
String parseChar = ".";
//GUI
ControlP5 cp5;
Textfield txtfldDistance, txtfldSpeed, txtfldTs, txtfldN,
txtfldFl, txtfldBron, txtfldPattern, txtfldTrun;
Button btnRun, btnStop, btnFrame;
//File I/O
PrintWriter writer;
String path;
//Runtime variables
int run = 0;
int createWriter = 0;
int frameCapture = 0;
int frameDisplay = 0;
//Time management
String timestamp;
int ms, ms_start;
final int frameX = 30;
final int frameY = 30;
void setup() {
frameRate(60);
time();
//Create GUI
textSize(20);
size(360,660);
//Create textboxes
cp5 = new ControlP5(this);
txtfldDistance = cp5.addTextfield("Distance[m]:")
.setPosition(30, 30)
.setSize(70, 30)
.setAutoClear(false)
.setText("0.5");
txtfldSpeed = cp5.addTextfield("Speed[rev/s]:")
.setPosition(30, 90)
.setSize(70, 30)
.setAutoClear(false);
txtfldTs = cp5.addTextfield("t_s[ms]")
.setPosition(30, 150)
.setSize(70, 30)
.setAutoClear(false)
.setText("10");
txtfldTrun = cp5.addTextfield("t_run[s]")
.setPosition(30, 210)
.setSize(70, 30)
.setAutoClear(false);
txtfldFl = cp5.addTextfield("f[mm]")
.setPosition(130, 30)
.setSize(70, 30)
.setAutoClear(false)
.setText("14");
txtfldBron = cp5.addTextfield("Bron[Watt]")
.setPosition(130, 90)
.setSize(70, 30)
.setAutoClear(false)
.setText("40");
txtfldPattern = cp5.addTextfield("Pattern[mm]")
.setPosition(130, 150)
.setSize(70, 30)
.setAutoClear(false)
.setText("random");
txtfldN = cp5.addTextfield("n")
.setPosition(130, 210)
.setSize(70, 30)
.setAutoClear(false)
.setText("1");
btnRun = cp5.addButton("Run")
.setPosition(230, 270)
.setSize(50,30)
.lock();
btnStop = cp5.addButton("Stop")
.setPosition(150, 270)
.setSize(50,30)
.lock();
btnFrame = cp5.addButton("Frame_Capture")
.setPosition(30, 270)
.setSize(90,30)
.lock();
//Create Serial COMM object
print(timestamp+"SERIAL PORT: ");
println(Serial.list());
// List all the available serial ports:
//arduino = new Serial(this, Serial.list()[2], 115200);
arduino = new Serial(this, Serial.list()[0], 115200);
arduino.clear();
arduino.bufferUntil('.');
}
void draw() {
time();
Frame_Capture();
display_frame();
if (frameDisplay == 1){
display_frame();
frameDisplay = 0;
println(timestamp+"---------------------");
}
}
int n = 0;
int[] frame_capture_data = new int[900];
void serialEvent(Serial arduino){
if (arduino.available() > 0){
//Parse serial data until '.'
data_rx = arduino.readStringUntil('.');
//Remove CR, LF and '.' from buffer
data_rx = data_rx.substring(2, data_rx.length()-1);
//print(n+":");
//println(data_rx);
if(data_rx.equals("Data transfer finished")){
println(timestamp+"Data transfer finished.");
println(timestamp+"Generating visual.");
frameCapture = 0;
frameDisplay = 1;
n = 0;
//unlock textfields
txtfldSpeed.unlock();
txtfldDistance.unlock();
txtfldTs.unlock();
txtfldBron.unlock();
txtfldPattern.unlock();
txtfldFl.unlock();
txtfldN.unlock();
btnRun.unlock();
btnStop.unlock();
btnFrame.unlock();
}else if(data_rx.equals("Ready")){
println(timestamp+"Device is ready.");
println(timestamp+"---------------------");
//unlock textfields
btnRun.unlock();
btnStop.unlock();
btnFrame.unlock();
}else if(data_rx.equals("Initialized")){
println(timestamp+"Device is initialized.");
}else if(data_rx.equals("Measurement finished")){
println(timestamp+"Measurement completed.");
Stop();
}else if(data_rx.equals("Frame capture")){
println(timestamp+"Frame capture transfer started.");
frameCapture = 1;
}else if(data_rx.equals("Frame capture failed")){
println(timestamp+"Frame capture failed. Try again.");
println(timestamp+"---------------------");
//unlock textfields
txtfldSpeed.unlock();
txtfldDistance.unlock();
txtfldTs.unlock();
txtfldBron.unlock();
txtfldPattern.unlock();
txtfldFl.unlock();
txtfldN.unlock();
btnRun.unlock();
btnStop.unlock();
btnFrame.unlock();
}else if(data_rx.contains("SQUAL")){
print(timestamp+"SQUAL: ");
println(data_rx.substring(5,data_rx.length()));
}else if(data_rx.equals("Time constant error")){
print(timestamp+"TIME CONSTANT ERROR");
}else if(frameCapture == 1 && n < 900){
frame_capture_data[n] = int(data_rx);
n++;
}else if(run == 1){
//print(data_rx);
writer.print(data_rx);
}
}
}
public void Run() {
/* When RUN is pressed program starts to run */
//Read value to determine path
float speed = float(txtfldSpeed.getText());
float distance = float(txtfldDistance.getText());
int t_s = int(txtfldTs.getText());
int bron = int(txtfldBron.getText());
int fl = int(txtfldFl.getText());
String pattern = txtfldPattern.getText();
String date = day()+"-"+month();
int n = int(txtfldN.getText());
// Create CSV data file, showing the results from experiment
if (speed > 0 && distance > 0){
if (createWriter == 0){
//Creating objects for writing to file
path = "data/"+date+"/x="+distance+"/"+"x="+distance+"_v="+speed+
"_ts="+t_s+"_f="+fl+"_bron="+bron+"_pat="+pattern+"_n="+n+".csv";
writer = createWriter(path);
//Runtime variables
createWriter = 1;
run = 1;
ms_start = millis();
//Transmit t_s en t_run
arduino.write("dt"+txtfldTs.getText());
arduino.write(parseChar);
arduino.write("trun"+int(txtfldTrun.getText())*1000);
arduino.write(parseChar);
//Transmit starting char to arduino
arduino.write("Run");
arduino.write(parseChar);
//Header
//writer.println("t_ard_ms,t_ard_us,dx,dy,ls");
//lock textfields
txtfldSpeed.lock();
txtfldDistance.lock();
txtfldTs.lock();
txtfldBron.lock();
txtfldPattern.lock();
txtfldFl.lock();
txtfldN.lock();
btnRun.lock();
btnStop.lock();
btnFrame.lock();
println(timestamp+"PROGRAM INITIATED");
println(timestamp+"File stored at: "+path);
}
//ERROR messages
} else if (speed <= 0 && distance <= 0){
println(timestamp+"ERROR: INVALID SPEED AND DISTANCE");
} else if (speed <= 0){
println(timestamp+"ERROR: INVALID SPEED");
} else if (distance <= 0){
println(timestamp+"ERROR: INVALID DISTANCE ");
} else if(txtfldSpeed.getText().equals("")){
println(timestamp+"ERROR: Enter paramaters.");
}
}
public void Stop() {
/* When STOP is pressed program terminates and writes to file */
if (createWriter == 1){
//Write to file and close stream
writer.flush();
writer.close();
//Runtime variables
run = 0;
createWriter = 0;
//unlock textfields
txtfldSpeed.unlock();
txtfldDistance.unlock();
txtfldTs.unlock();
txtfldBron.unlock();
txtfldPattern.unlock();
txtfldFl.unlock();
txtfldN.unlock();
btnRun.unlock();
btnStop.unlock();
btnFrame.unlock();
txtfldN.setText(str(int(txtfldN.getText())+1));
if (int(txtfldN.getText()) > 5){
txtfldN.setText("1");
txtfldSpeed.clear();
}
println(timestamp+"Data written to file.");
println(timestamp+"---------------------");
}
}
public void Frame_Capture() {
arduino.write("Frame capture run");
arduino.write(parseChar);
//lock textfields
txtfldSpeed.lock();
txtfldDistance.lock();
txtfldTs.lock();
txtfldBron.lock();
txtfldPattern.lock();
txtfldFl.lock();
txtfldN.lock();
btnRun.lock();
btnStop.lock();
btnFrame.lock();
}
void display_frame(){
int[] frame1 = new int[225];
int[] frame2 = new int[255];
int x = 30;
int y = 320;
//resolutie 10x10
int s = 10; // size of pixel, i.e. side lengths
//Max res is 30x30
int sz = 10;
int res = 30;
for (int i = 0; i < 15; i++){
for (int m = 0; m < 15; m++){
frame1[15*i+m] = frame_capture_data[30*i+m];
frame2[15*i+m] = frame_capture_data[30*i+m+15];
}
}
//for (int i = 0; i < res*res; i++){
//Commented by Daan:
//for (int j = 0; j < res; j++){ // j resembles the column index.
// for (int k = 0; k < res; k++){ // k resembles the row index
// //fill(map(frame_capture_data[30*j+k],0,63,0,255));
// //frame_capture_data[30*j+k] = 300; // test to see how the pixel values can be manipulated
// fill(float(frame_capture_data[30*j+k]));
// rect(x+j*10, y+300-k*10, s, s);
// //println(frame_capture_data[30*j+k]);
// }
//}
for( int i = 0; i < 900; i++ )
{
fill( map(frame_capture_data[i], 0, 63, 0, 255) ); // Convert from ADNS greyscale to 0 - 255 grey scale format.
rect(x + (i / frameX * sz), // Each rect() is a rectangle that represents a pixel. I.e. width and height of each pixel is "sz".
y +300 - (i % frameY * sz),
sz, sz);
// //rect(off_x + (i % frameX * sz), // Each rect() is a rectangle that represents a pixel. I.e. width and height of each pixel is "sz".
// //off_y + (i / frameY * sz),
// //sz, sz);
}
fill(255,0,0);
rect(x+3*10, y+300-8*10, s, s); // this is red test dot, j = 3 (column), k = 8 (row).
// I.e. this is the 30*3 + 8 = 98 th pixel in frame stream from sensor.
}
public void time(){
/* Keeps track of time
Creates timestamp for messages*/
String h = str(hour());
String m = str(minute());
String s = str(second());
if (int(h) < 10){
h = "0"+h;
} else if(int(m) < 10){
m = "0"+m;
} else if(int(s) < 10){
s = "0"+s;
}
timestamp = "["+h+":"+m+":"+s+"] ";
}

Arduino - Adafruit 16-channel board, how to properly control all channels with less delay?

I am trying to control a few (8 for now) servo motors using this 16-channel board. I am running to some issues about accuracy, for example, when moving a couple of motors do draw a diagonal line, because of the delay between each servo, each motor will move in different timing resulting in incorrect drawings.
I am not sure about how to drive the motors in the fastest way in therms of code.
Where to set delays, the baud rate settings for this application, etc. I couldn't find a good example using all channels with minimum delay. In my case, messages are coming from serial, as explained in the code comment.
Is this the right way to drive this board channels?
I am using an arduino uno, but I would like to check if using a Teensy 3.2 results in best performances for this application.
Thanks in advance for any suggestions.
#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>
//#define SERVOMIN 150
//#define SERVOMAX 600
// temporary setting pins for 4 lights - it will be controlled by some decade counter...
//#define L1 4
//#define L2 7
//#define L3 8
//#define L4 10
#define L1 9
#define L2 10
#define L3 11
#define L4 12
/*
* a "pointer" device includes a light and 2 servos. Parameters from serial are:
* index,light,servo1,servo2; <- parameters separated by ',' end of pointer is ';'
*
* example of how serial is coming containing instructions for 4 pointers;
0,0,180,180;1,0,0,0;2,0,180,180;3,0,0,0;
0,0,90,90;1,0,90,90;2,0,90,90;3,0,90,90;
**most of the time these instructions doesn't come all for 4 pointers.
ex:
1,0,12,12;4,255,100,100;
**sometimes it comes only id and light parameter.
0,255;1,0;
(instructions only to turn light on/off)
*/
//values for 8 servos:
const uint8_t SERVOMIN[] = {150, 130, 150, 130, 150, 130, 150, 130};
const uint8_t SERVOMAX[] = {600, 500, 600, 500, 600, 500, 600, 500};
//boards (for now, only one board = 16 servos)
Adafruit_PWMServoDriver pwm [] = {
Adafruit_PWMServoDriver(0x40)
};
uint8_t servonum = 0;
uint8_t activeServos = 4; //not being used now
char buf [4]; //maybe too long
uint16_t currentPointer [4]; //index//light//servo1//servo2
byte lightPin [4] = {L1, L2, L3, L4};
uint8_t lightstatus [4] = {0, 0, 0, 0};
//debug
String inputString = ""; // a string to hold incoming data
boolean stringComplete = false; // whether the string is complete
boolean feedback = false;
void setup() {
//temporally as digital outputs
pinMode(L1, OUTPUT);
pinMode(L2, OUTPUT);
pinMode(L3, OUTPUT);
pinMode(L4, OUTPUT);
Serial.begin(115200);//230400 //115200 //57600 //38400 ?
for ( uint8_t i = 0; i < sizeof(pwm); i++) {
pwm[i].begin();
pwm[i].setPWMFreq(60);
}
}
void loop() {
reply();
}
void reply() {
if (stringComplete) {
if (feedback) Serial.println(inputString);
// clear the string:
inputString = "";
stringComplete = false;
for ( int i = 0; i < sizeof(buf); ++i ) buf[i] = (char)0;
}
}
void serialEvent() {
static byte ndx = 0;
static int s = 0;
while (Serial.available()) {
char rc = (char)Serial.read();
inputString += rc;
//(2) setting pointer parameter
if ( rc == ',') {
setPointer(s);
s++;
for ( int i = 0; i < sizeof(buf); ++i ) buf[i] = (char)0;
ndx = 0;
}
//(3) end of this pointer instruction
else if (rc == ';') {
setPointer(s);
//executePointer(); //plan B
ndx = 0;
s = 0;
for ( int i = 0; i < sizeof(buf); ++i ) buf[i] = (char)0;
}
//(4) end of command line
else if (rc == '\n') {
//p = 0;
s = 0;
stringComplete = true;
}
//(1) buffering
else {
buf[ndx] = rc;
ndx++;
}
}
}
void setPointer(int s) {
//index//light//servo1//servo2
int value;
value = atoi(buf);
//index
if (s == 0) {
if (feedback) {
Serial.print("index:");
Serial.print(value);
Serial.print(", buf:");
Serial.println(buf);
}
currentPointer[0] = value;
}
//light
else if (s == 1) {
int index = currentPointer[0];
currentPointer[s] = value;
//Serial.println(index);
digitalWrite(lightPin[index], (value > 0) ? HIGH : LOW);
// analogWrite( lightPin[currentPointer[0]], currentPointer[1]); // implement later
if (feedback) {
Serial.print("light: ");
Serial.println(value);
}
//servos
} else {
int index = currentPointer[0];
if (feedback) {
Serial.print("servo ");
Serial.print(index * 2 + s - 2);
Serial.print(": ");
Serial.println(value);
}
uint16_t pulselen = map(value, 0, 180, SERVOMIN[index], SERVOMAX[index]);
currentPointer[s] = pulselen;
pwm[0].setPWM(index * 2 + (s - 2), 0, pulselen); //current pointer id * 2 + s (s is 2 or 3)
//delay(20);
}
}
// this was plan B - not using
void executePointer() {
int index = currentPointer[0];
analogWrite( lightPin[index], currentPointer[1]);
pwm[0].setPWM(index * 2, 0, currentPointer[2]);
pwm[0].setPWM(index * 2 + 1, 0, currentPointer[3]);
delay(20);
}

Arduino - PD - Line Following

#include <QTRSensors.h>
#define NUM_SENSORS 5 //Number of sensors used
#define NUM_SAMPLES_PER_SENSOR 4 //Average 4 analog samples per sensor reading
#define EMITTER_PIN 13 //Emitter is controlled by digital pin 2
QTRSensorsAnalog qtra((unsigned char[]) {0,1,2,3,4},
NUM_SENSORS, NUM_SAMPLES_PER_SENSOR, EMITTER_PIN);
unsigned int sensorValues[NUM_SENSORS]; //Variable Array for sensor values
const double kp = .505; //Variable for adjusting KP Value
const double kd = 150; //Variable for adjusting KD Value
const int max_speed = 255; //Variable for the Maximum Speed of the Motors
unsigned int position = 0; //Variable for holding the calculated position
int set_point = 1500;//Variable for holding the value of the center of the line
int error = 0; //Variable for holding the error from center of line based on position
int last_error = 0; //Variable for holding the previous error
int white = 0; //Variable for reading the line over white or black
int max_difference = 80 ;
double spd_right; //Speed for the Right Motor
double spd_left; //Speed for the Left Motor
int derivative = 0; //Value for the derivative
int error_value = 0; //Value for the error_value calculated from the pd function
int key_s6 = 2;//Declaring Digital Push Button
int E1 = 5; //M1 Speed Control
int E2 = 6; //M2 Speed Control
int M1 = 4; //M1 Direction Control
int M2 = 7; //M2 Direction Control
int sensors_sum = 0;
//Setup Method that includes calibration
void setup(){
pinMode(4, OUTPUT);
pinMode(5, OUTPUT);
pinMode(6, OUTPUT);
pinMode(7, OUTPUT);
pinMode(13, OUTPUT);
calibration();
set_Point();
}//Ends Setup
void loop (){
get_sum();
white_or_black();
pid_calc();
adjust_control();
set_motors();
}//Ends Loop*/
/*Method for calibrating set_point on individual tracks
*/
void set_Point(){
qtra.readCalibrated(sensorValues);
for (int i = 0; i <= 5; i++){
set_point = sensorValues[i] + set_point;
}//Ends For
}//Ends set_point Method
/*Method for Calibrating the sensors to the ambiance lighting and
* varying contrasts of each individual track.
*/
void calibration (){
for (int i = 0; i<250; i++){
qtra.calibrate();
delay(20);
}//Ends For
}//Ends Calibration Method
/*Method for calculating the sum of all the sensors to be used in the
* white_or_black method for determining the background color of the track
*/
void get_sum(){
qtra.readCalibrated(sensorValues);
for (int i = 0; i <= 5; i++){
sensors_sum = sensorValues[i] + sensors_sum;
}//Ends For
}//Ends get_sum Method
/*Method for determining the background color of the current portion of the
* track based off of the value returned by get_sum and adjusting a variable
* to follow a white of black line as necessary.
*/
void white_or_black(){
if (sensors_sum > 2000){
white = 1;
}//Ends If
else{
white = 0;
}//Ends Else
}//Ends white_or_black Method
/*Method for following the line based of the position, proportional,
* and derivative
*/
void pid_calc(){
position = qtra.readLine(sensorValues, QTR_EMITTERS_ON, white);
error = position - set_point;
derivative = error-last_error;
last_error = error;
error_value = int(error*kp+derivative*kd);
}//Ends pid_calc Method
void adjust_control(){
if (error_value>max_difference){
error_value = max_difference;
}//Ends If
else if(error_value < max_difference){
error_value = -max_difference;
}//Ends If
if (error_value < 0 ){
spd_right = max_speed + error_value;
spd_left = max_speed;
}//Ends If
else{
spd_right = max_speed;
spd_left = max_speed - error_value;
}//Ends Else
}//Ends adjust_control Method
void set_motors(){
analogWrite(M1, HIGH);
analogWrite(M2, HIGH);
analogWrite(E1, constrain(spd_right, 0, max_speed));
analogWrite(E2, constrain(spd_left, 0, max_speed));
}//Ends set_motors Method
void stop(){
analogWrite(E1, 0);
analogWrite(E2, 0);
}//Ends Stop Method
I'm new to the arduino language. This is my first program and I wrote it last night. It's a PD Line Following example.
I'm currently having issues with the motors adjusting. Simply put it just goes straight.
I'm using a QTR Analog Sensor Array. I might switch to digital because one of the analog inputs on the romeo board I have is not working. Does anyone one have any suggestions or improvements to my logic?
void set_Point(){
qtra.readCalibrated(sensorValues);
for (int i = 0; i < NUM_SENSORS; i++){
set_point += sensorValues[i];
}
}
A strange thing about this loop is that is set_point is not initialised (to zero) before adding, and not divided (by i==NUM_SENSORS-=5) after the summation.

Resources