Arduino PID DC Motor Position Control - arduino

I'm doing a control engineering project, implementing a PID motor position control for automatic antenna tracking system. The system contain a dc motor, absolute encoder, and a motor driver.
Everything work as expected, but one thing. The motor cannot stop at set point value near 0 degree (350 - 359, 0 - 10 degree). The used code:
#include <PID_v1.h>
int RPWM = 5;
int LPWM = 6;
int L_EN = 7;
int R_EN = 8;
boolean pin_state[10];
byte input_pin[] = {1, 2, 3, 4, 9, 10, 11, 12, 13};
int dec_position = 0;
int dc = 0;
double kp = 50, ki = 45, kd = 2;
double input = 0, output = 0, setpoint = 0;
volatile long encoderPos = 0;
PID myPID(&input, &output, &setpoint, kp, ki, kd, DIRECT);
void setup() {
pinMode(L_EN, OUTPUT);
pinMode(R_EN, OUTPUT);
pinMode(RPWM, OUTPUT);
pinMode(LPWM, OUTPUT);
for (byte i = 0; i < 9; i++) {
pinMode(input_pin[i], INPUT);
}
TCCR1B = TCCR1B & 0b11111000 | 1;
myPID.SetMode(AUTOMATIC);
myPID.SetSampleTime(1);
myPID.SetOutputLimits(-255, 255);
digitalWrite(L_EN, HIGH);
digitalWrite(R_EN, HIGH);
}
void loop() {
if (Serial.available() > 0) {
String baca = Serial.readString();
setpoint = baca.toInt();
}
ReadEncoder();
input = dc;
myPID.Compute();
pwmOut(output);
}
void pwmOut(int out) {
if (out > 0) {
analogWrite(RPWM, out);//Sets speed variable via PWM
}
else {
analogWrite(LPWM, abs(out));//Sets speed variable via PWM
}
}
void ReadEncoder() {
// FOR READING ENCODER POSITION, GIVING 0-359 OUTPUT CORRESPOND TO THE ENCODER POSITION
for (byte i = 0; i < 9; i++) {
pin_state[i] = !(digitalRead(input_pin[i]));
}
dec_position = (pin_state[8] * 256) + (pin_state[7] * 128) + (pin_state[6] * 64) + (pin_state[5] * 32) + (pin_state[4] * 16) + (pin_state[3] * 8) + (pin_state[2] * 4) + (pin_state[1] * 2) + pin_state[0];
dc = map(dec_position, 0, 500, 0, 360);
}
When the set point is a value between 10 - 350 the sytem worked well. But when it is not, the motor never stop rotating.
I know the problem is due to a little position overshoot cause the encoder to read a very large error.
For instance, if the setpoint is 0 degree, the motor rotate to reach it. Motor rotation is slowing down as its "now" position approaching 0 degree, but the system is not overshoot free. Therefore, even 1 degree overshoot cause the error value is -359 (set point - now position) and the motor rotate again to reach the desired position.
Need help how to overcome this problem. Sorry for bad english.

Here's the solution
double error;
if (SP>PV) {
if (abs(SP-PV) < abs(-360 + SP - PV)) error = SP - PV;
else error = -360 + SP - PV;
}
else{
if(abs(SP-PV)< abs(360 - PV + SP)) error = SP - PV;
else error = 360 - SP + PV;
}
Instead off simple present value minus the set point for error. The code above return shortest path from present value to set point.

i didn't read your code yet. However, to reach "set point" (SV) you should give for "present value" (PV) an error allowance (EA)
For example: EA = SV - PV.
If EA = (-2,+2)degree then it reach "now position"
And, you should not use degree for angle, you should convert it to position (calculate by pulse)
Hope that concept can help you.

Related

Arduino nano data points missing

thanks for helping me out with my previous problem.
I am now able to get the data from the arduino to PC using putty with the FSR's attached to a sole and detecting pressures.
The only problem I am facing now is that it seems to be missing instances of Heel strike and Toe Off every now and then.(Image Attached)Plot of data
Blue line represents data from Heel FSR and Red line for Toe FSR
It can be seen from the picture that the arduino is missing a Heel Strike at blue coloured peak No 3 and a toe OFF instant at red peak 5 from the end.
Can any one tell me why this is happening regularly..
I am using arduino nano with baudrate 9600 and 2 FSR's ..On increasing the baudrate above 38400 the loss seems to occur more often
/** Code finds instances of toe off and heel strike using data from FSR's*/
void setup()
{
/*************************************************SERIAL**************************************************/
Serial.begin(9600);
}
/*Variables for FSRS*/
//A0= Toe,A1=Heel
int sum_toe_max = 0,sum_toe_min = 0, sum_heel_max = 0, sum_heel_min = 0 ,toe, heel, temp,diff_toe,diff_heel,data_heel=0, data_toe=0, data_toe2, data_heel2 ;
int heel_max[5] = {0, 0, 0, 0, 0}, toe_max[5] = {0, 0, 0, 0, 0}; /*These arrays holds the top 5 maximas upto the time of calibration*/
int heel_min[5]= {100,100,100,100,100}, toe_min[5]={100,100,100,100,100};/*These arrays holds the top 5 maximas upto the time of calibration*/
float avg_heel_max,avg_heel_min,avg_toe_max,avg_toe_min;/*variables to hold the averages of the valus of max and min arrays*/
float UL=0.80,LL=0.05;/* Setting the Limiters*/
int counter_toe_max = 0, counter_heel_max = 0,counter_toe_min = 0, counter_heel_min = 0;//counter for the number of H and T occured
int cal_limit = 10;/*time for which calibration should go on*/
int timer = 0;/*stores the time elapsed*/
void loop()
{
read_FSR();//Call The FSR function
//read_acc();
}
/*********************************************FSR_TOE***********************************************/
/*Function to read the FSR_TOE data and save to SD*/
void read_FSR()
{
data_toe = analogRead(A0);
data_heel = analogRead(A1);
timer = millis() / 1000;
Serial.print(millis());
Serial.print(" ");
Serial.print(data_toe);
Serial.print(" ");
Serial.println(data_heel);
/*Calibration and finding the maxima uptil cal_limit seconds.*/
if (timer < cal_limit)
{
/*TOE calibration*/
/*To find the top 5 max pressures*/
if (data_toe > toe_max[counter_toe_max])
{
toe_max[counter_toe_max] =data_toe;
counter_toe_max = counter_toe_max + 1;
}
if (counter_toe_max >= 5)
{
counter_toe_max = 0;
}
/*To find the top 5 min pressures*/
if (data_toe < toe_max[counter_toe_min])
{
toe_min[counter_toe_min] = data_toe;
counter_toe_min = counter_toe_min + 1;
}
if (counter_toe_min >= 5)
{
counter_toe_min = 0;
}
/*HEEL FSR calibration*/
/*To find the top 5 max pressures*/
if (data_heel > heel_max[counter_heel_max])
{
heel_max[counter_heel_max] =data_heel;
counter_heel_max = counter_heel_max + 1;
}
if (counter_heel_max >= 5)
{
counter_heel_max = 0;
}
/*To find the top 5 min pressures*/
if (data_heel < heel_min[counter_heel_min])
{
heel_min[counter_heel_min]=data_heel; =
counter_heel_min = counter_heel_min + 1;
}
if (counter_heel_min >= 5)
{
counter_heel_min = 0;
}
}
/*Displaying the maximum and minimum valus array and finding the averages for both the FSR's*/
if (timer == cal_limit && temp == 0)
{
/*Finding sum of the array elements*/
for (int i = 0; i < 5; i++)
{
sum_toe_max = sum_toe_max + toe_max[i];
sum_toe_min = sum_toe_min + toe_min[i];
}
for (int i = 0; i < 5; i++)
{
sum_heel_max = sum_heel_max + heel_max[i];
sum_heel_min = sum_heel_min + heel_min[i];
}
avg_toe_max = sum_toe_max / 5;/*dividing by 5 to get the avg of the 5 values*/
avg_toe_min = sum_toe_min / 5;
avg_heel_max = sum_heel_max / 5;
avg_heel_min = sum_heel_min / 5;
diff_toe = avg_toe_max-avg_toe_min;/*difference between maximas and minimas*/
diff_heel = avg_heel_max-avg_heel_min ;
Serial.println();
Serial.print(F("Avg ToePress max "));
Serial.println(avg_toe_max);
Serial.print(F("Avg ToePress min "));
Serial.println(avg_toe_min);
Serial.print(F("Avg HeelPress max "));
Serial.println(avg_heel_max);
Serial.print(F("Avg HeelPress min "));
Serial.println(avg_heel_min);
Serial.print(F("Diff in avg toe press:"));
Serial.println(diff_toe);
Serial.print(F("Diff in avg heel press:"));
Serial.println(diff_heel);
Serial.print(F("Target HeelPress "));
Serial.println(UL*(avg_heel_max-avg_heel_min));
Serial.print(F("Target ToePress "));
Serial.println(LL*(avg_toe_max-avg_toe_min));
temp = temp + 1;
}
/*Done with calibration( when timer =10s)*/
/*Checking the oncoming data for a condition of Toe Off
Consider it as a toe off if the normalised value of data_toe i.e (data_toe-avg_toe_min)/diff_toe
at the previous instant is greater than LL(Lower Limit) i.e 0.2 times the differnce between the averages of max and min of toe FSR
and the normalised value of data_toe2 at the current instant is lesser than the same*/
if (timer > cal_limit && (data_toe-avg_toe_min)/diff_toe > LL)
{
data_toe2 = analogRead(A0);/*Data toe at the current instant*/
if ((data_toe2-avg_toe_min)/diff_toe < LL)
{
//
Serial.print(timer*1000);
Serial.print(" ");
Serial.print(("f t T "));
Serial.print(data_toe);
Serial.print("Avg min ");
Serial.print(avg_toe_min);
Serial.print("Diff ");
Serial.print(diff_toe);
Serial.println(" TOE");
}
}
/*Checking the oncoming data for a condition of Heel Stike
Consider it as a Heel Strike if the normalised value of data_heel i.e (data_heel2-avg_heel_max)/diff_heel
at the previous instant is lesser than UL(Lower Limit) i.e 0.8 times the differnce between the averages of max and min of heel FSR
and the normalised value of data_heel2 at the current instant is greater than the same*/
if(timer>cal_limit && (data_heel-avg_heel_min)/diff_heel<=UL)
{
data_heel2=analogRead(A1);/*Data heel at the current instant*/
if((data_heel2-avg_heel_min)/diff_heel>=UL)
{
Serial.print(timer*1000);
Serial.print(" ");
Serial.print(("f t H "));
Serial.print(data_heel);
Serial.print(" HEEL");
Serial.println(UL);
Serial.flush();
}
}
}
You define String arr[2][2]={{""}}; but access it later with arr[SD_savr][3]="TO";, arr[SD_savr][4]=Y[0]; and arr[SD_savr][3]="HS";, which is out of bounds and causes undefined behavior, which is the possible cause of your resets.
As you removed SD usage, could you also remove the #include
Does that influence your memory requirements? ( Or even the bad behaviour ? )
Freezing instead of Reset is usually the same cause, just hitting some other address. ;)
I cannot not see any array out of bounds issue with the current code, but that's where I'd double/triple check...
Other questions:
I2C (Wire): you request 14 bytes, but read only 2 of them ? ...
start is a float ? dt should be 0.0, I guess

Arduino Wheel Speed Sensor Detect Direction

I'm trying to develop a wheel counter that determines both rotation rate, and the direction of rotation.
I have two magnetic pickup sensors mounted close together, and two metal chunks on the wheel 180 degrees apart.
Currently I have been able to measure rotation rate and convert this to distance, and the distance shown looks good. Here is the code for that:
volatile byte half_revolutions;
volatile byte half_revolutions_b;
volatile unsigned long last_time_a;
volatile unsigned long last_time_b;
volatile byte dir;
unsigned int rpm;
unsigned int rpm_b;
unsigned long timeold;
unsigned long timeold_b;
unsigned long time_print;
double distance = 0.0;
void setup()
{
Serial.begin(9600);
attachInterrupt(0, rpm_fun, RISING);
attachInterrupt(1, rpm_fun_b, RISING);
half_revolutions = 0;
half_revolutions_b = 0;
rpm = 0;
timeold = 0;
timeold_b = 0;
time_print = 0;
dir = 1;
}
void loop()
{
int rpm_guess;
if (half_revolutions >= 2)
{
rpm = 30*1000/(millis() - timeold)*half_revolutions;
timeold = millis();
half_revolutions = 0;
}
else if (millis() - timeold > 1000)
{
rpm = 0;
}
if (half_revolutions_b >= 2)
{
rpm_b = 30*1000/(millis() - timeold_b)*half_revolutions_b;
timeold_b = millis();
half_revolutions_b = 0;
}
else if (millis() - timeold_b > 1000)
{
rpm_b = 0;
}
if (millis() - time_print > 500)
{
rpm_guess = ((int)rpm + (int)rpm_b) / 2.0;
double rad_per_sec = (6.0*3.14159* rpm_guess)/180.0;
double metres_per_sec = rad_per_sec*0.038;
distance += metres_per_sec * 0.5;
Serial.print((int)last_time_b - (int)last_time_a);
Serial.print(",");
Serial.println(distance);
time_print = millis();
}
}
void rpm_fun()
{
half_revolutions++;
last_time_a = micros();
//Each rotation, this interrupt function is run twice
}
void rpm_fun_b()
{
half_revolutions_b++;
last_time_b = micros();
//Each rotation, this interrupt function is run twice
}
I was hoping to use the fact that sensor A should lead sensor B if rotation is clockwise, and vice-versa if anti-clockwise. However my logic doesn't seem to be working properly, Serial.print((int)last_time_b - (int)last_time_a); seems to switch between positive and negative no matter the direction I'm travelling.
I'd really appreciated any help with this.
I would rather do the direction guess in the interrupt handler:
void rpm_fun() {
if (last_time_a > last_time_b) dir = 0;
else dir = 1;
half_revolutions++;
last_time_a = micros();
}
void rpm_fun_b() {
if (last_time_a > last_time_b) dir = 0;
else dir = 1;
half_revolutions_b++;
last_time_b = micros();
}
If you experience "bounces", you could add some "debouncing" code.

Get 3 Values from processing to Arduino

I have a color tracking program in Processing, which works with a Kinect. When I click somewhere in the picture it saves this color and draws an ellipse around it. I just want to send 3 int values (one for red, green and blue) over myPort.write() to Arduino and save these 3 values in Arduino in 2 variables. My goal is to light a red LED if the red variable is the highest, and the green LED if green is the highest and so on.
I've tried several examples I found whiel googling, but nothing works. I don't know how Arduino should get the correct values in the variables!
EDIT: Here you have my Processing code. I glued it together from several other tutorials until I nearly cried..
import processing.serial.*;
Serial myPort;
import SimpleOpenNI.*;
SimpleOpenNI kinect;
// Frame
PImage currentFrame;
color trackColor;
int r1, g1, b1, r2, g2, b2;
void setup()
{
size(640, 480);
String portName = Serial.list()[0]; //change the 0 to a 1 or 2 etc. to match your port
myPort = new Serial(this, portName, 9600);
kinect = new SimpleOpenNI(this);
kinect.enableRGB();
trackColor = color (255, 0, 0);
smooth ();
currentFrame = createImage (640, 480, RGB);
}
void draw()
{
kinect.update();
currentFrame = kinect.rgbImage ();
image(currentFrame, 0, 0);
currentFrame.loadPixels();
// Before we begin searching, the "world record" for closest color is set to a high number that is easy for the first pixel to beat.
float worldRecord = 500;
// XY coordinate of closest color
int closestX = 0;
int closestY = 0;
// Begin loop to walk through every pixel
for (int x = 0; x < currentFrame.width; x ++ ) {
for (int y = 0; y < currentFrame.height; y ++ ) {
int loc = x + y*currentFrame.width;
// What is current color
color currentColor = currentFrame.pixels[loc];
r1 = (int)red(currentColor);
g1 = (int)green(currentColor);
b1 = (int)blue(currentColor);
r2 = (int)red(trackColor);
g2 = (int)green(trackColor);
b2 = (int)blue(trackColor);
// Using euclidean distance to compare colors
float d = dist(r1, g1, b1, r2, g2, b2); // We are using the dist( ) function to compare the current color with the color we are tracking.
// If current color is more similar to tracked color than
// closest color, save current location and current difference
if (d < worldRecord) {
worldRecord = d;
closestX = x;
closestY = y;
}
}
}
// We only consider the color found if its color distance is less than 10.
// This threshold of 10 is arbitrary and you can adjust this number depending on how accurate you require the tracking to be.
if (worldRecord < 10) {
// Draw a circle at the tracked pixel
fill(trackColor);
strokeWeight(4.0);
stroke(0);
ellipse(closestX, closestY, 30, 30);
}
if (mousePressed == true) {
color c = get(mouseX, mouseY);
//println("r: " + red(c) + " g: " + green(c) + " b: " + blue(c));
// Save color where the mouse is clicked in trackColor variable
int loc = mouseX + mouseY*(currentFrame.width);
trackColor = currentFrame.pixels[loc];
println("red " + r2);
println("green " + g2);
println("blue " + b2);
int colors[] = {r2, g2, b2};
for(int i=0; i < 3; i++) {
myPort.write(colors[i]);
}
}
println("ClosestX " + closestX);
myPort.write(closestX);
}
And my Arduino Code, where I don't know how to get several values.
int val;
int ledPin = 13;
int freq;
int piezoPin = 9;
int redLED = 3;
int greenLED = 5;
int blueLED = 7;
int red, green, blue;
void setup() {
pinMode(ledPin, OUTPUT); // Set pin as OUTPUT
Serial.begin(9600); // Start serial communication at 9600 bps
digitalWrite(ledPin, LOW);
}
void loop() {
if (Serial.available() > 0)
{ // If data is available to read,
val = Serial.read(); // read it and store it in val
}
if(red > green && red > blue) {
digitalWrite(redLED, HIGH); //light Red LED
}
if(green > red && green > blue) {
digitalWrite(greenLED, HIGH); //light Red LED
}
if(blue > red && blue > green) {
digitalWrite(blueLED, HIGH); //light Red LED
}
//Piezo buzzing higher when X-Position of tracked color is higher.
if (val < 100) {
freq = 50;
}
else if (val < 200) {
freq = 200;
}
else if (val < 300) {
freq = 400;
}
else if (val < 400) {
freq = 600;
}
else if (val < 500) {
freq = 800;
}
else (freq = 1000);
tone(piezoPin, freq);
}
EDIT2: Yes, additionally to lighing the LEDs I also want to have a sound from a piezo buzzer, but that works pretty well, so no questions on that... yet.
Help, please!!
Serial communication to your arduino works with a single byte at a time.
As luck would have it, the three components of a Processing Color are also three bytes.
One for red(0-255)
One for green(0-255)
One for blue(0-255)
Now all we need is a little more info so we can keep them separate.
Because a byte's minimum and maximum values are 0-255, there's no safe character we can use to keep track of the three different bytes, so we need a way to figure out where the info we send begins and ends.
An easy way to do this, is to set up a header and a footer for your messages ; something like :
<color>[byte (red)][byte (green)][byte (blue)]</color>
If we are going to read and decipher messages formatted like this, we are going to need a little buffer that will store the values we receive from Processing, so we can read them back and see if we can match the message format.
So, on the Arduino side, we need this :
String buffer = "";
String messageBegin = "<color>";
String messageEnd = "</color>";
//we read our serial data in the SerialEvent() function
//this is called *after* a loop(), and only if there is serial data in the buffer.
void serialEvent()
{
while(Serial.available())
{
buffer += (char)Serial.read();
}
}
void loop()
{
//now, inside loop, we no longer need to worry about gathering data from serial.
//we do still need to figure out if our message is complete, and then parse it.
//if our buffer contains both the beginning and the end of a message
//in the right order.
int beginIndex = buffer.lastIndexOf(messageBegin);
int endIndex = buffer.lastIndexOf(messageEnd);
if(beginIndex != -1 && endIndex != -1 && beginIndex < endIndex)
{
//we have a complete message!
//our red color starts 7 characters after where the message begins,
//because our "messageBegin" is 7 characters long
string lastMessage = buffer.substring(beginIndex+7);
//this is arguably not the prettiest way to get our byte values back.
//see if you can do better for bonus points!
byte messageAsBytes[80];
lastMessage.getBytes(messageAsBytes, messageAsBytes.length());
//we can now finally reconstruct the value we had from processing!
byte r = (byte)messageAsBytes[0];
byte g = (byte)messageAsBytes[1];
byte b = (byte)messageAsBytes[2];
//if we get a complete message, we can clear our buffer. (don't forget to do this!)
buffer = "";
}
}
On the processing side, all we need to do is make sure our messagebegin and messageend are sent along for the ride :
myPort.write("<color">);
for(int i=0; i < 3; i++) {
myPort.write(colors[i]);
}
myPort.write("</color">);

Unexpected behavior in my RGB-strip driver code

I'm getting wrong output on the pins 9, 10 and 11. They are meant to be inputs for my RGB strip driver circuit. Which is basically an array of NPN transistors pulling current from the strip.
The basic idea is to get only 2 controls to set R, G, B and brightness. I'm using a button and a potenciometer. Potenciometer is used to set the value and the button to skip to next value setting. There is one setting state which is like the default one. It is the one to set the brightness and I will be using this most of the time. The othe ones are for setting the colors and when in one of those setting the strip will be blinking in between only the color I'm currently setting and the result with alle three colors set. The whole code was working just fine until I added the brightness setting and I think that I am kinda lost in my own code.
I even added a serial to read the outputs but I don't understand why are the numbers the way they are :(
int pinR = 9;
int pinG = 10;
int pinB = 11;
int potPin = A0;
const int buttonPin = 2;
int brightR = 0;
int brightG = 0;
int brightB = 0;
int brightness = 50; //
int R;
int G;
int B;
int potValue = 0;
int blinky = 0;
boolean blinking = false;
int buttonState;
int lastButtonState = LOW;
long lastDebounceTime = 0;
long debounceDelay = 50;
int setting = 0; //0=R 1=G 2=B 3=Brightness
void setup() {
// put your setup code here, to run once:
pinMode(pinR, OUTPUT);
pinMode(pinG, OUTPUT);
pinMode(pinB, OUTPUT);
Serial.begin(9600);
}
void RGBset(int r, int g, int b){
analogWrite(pinR, r);
analogWrite(pinG, g);
analogWrite(pinB, b);
}
void loop() {
// put your main code here, to run repeatedly:
potValue = analogRead(potPin);
potValue = map(potValue, 0, 1023, 0, 255); //read pot --> map to values from 0 - 255
int reading = digitalRead(buttonPin);
if (reading != lastButtonState) {
lastDebounceTime = millis();
}
if ((millis() - lastDebounceTime) > debounceDelay) {
if (reading != buttonState) {
buttonState = reading;
if (buttonState == HIGH) {
setting++;
}
}
}
lastButtonState = reading;
if(setting > 3){ // 0=R 1=G 2=B 3=Brightness
setting = 0; // cant get 4 cause there is no 4
}
if(setting == 0){
brightR = potValue;
if(blinking){
RGBset(brightR, brightG, brightB);
}else{
RGBset(brightR, 0, 0);
}
}
if(setting == 1){
brightG = potValue;
if(blinking){
RGBset(brightR, brightG, brightB);
}else{
RGBset(0, brightG, 0);
}
}
if(setting == 2){
brightB = potValue;
if(blinking){
RGBset(brightR, brightG, brightB);
}else{
RGBset(0, 0, brightB);
}
}
if(setting == 3){
brightness = potValue;
brightness = map(brightness, 0, 255, 1, 100); //mapping brightness to values from 1 - 100
R = brightR * brightness / 100; //set value * brightness / 100
G = brightG * brightness / 100; //that leads to get % of set value
B = brightB * brightness / 100; //255 * 50 / 100 = 127,5 ==> 128
RGBset(R, G, B); //it wont blink in thiss setting
}
if(setting != 3){
blinky++;
if(blinky > 1000){
blinking = !blinking;
blinky = 0;
}
}
String output = (String(brightR) + " " + String(R) + " " + String(brightG) + " " + String(G) + " " + String(brightB) + " " + String(B) + " " + String(brightness) + " " + String(potValue) + " " + String(blinking));
Serial.println(output);
delay(1);
}
First, in setup:
pinMode(buttonPin , INPUT);
Second, what are you expected when setting==3? You aren't reloading/updating the variables for brightR brightG brightB. So, when you change setting, you will lost the change of brightness

Arduino function to fade an RGB from one color to the next

For my Arduino project I have a Neopixel RGB Strip with 72 LED's.
I can successfully change the colour of any of the LED's (at the moment I'm only setting the first one 0 for testing purposes) so I know my wiring isn't the problem here, it's my coding.
What I would like to do is be able to select a color and then another color and have the first color fade to the next color and so forth (much like the LIFX bulbs behave when using the iPhone application).
This is what I have at the moment:
I am logging output of all the variables to give you an indication of what's going on. I'm not 100% sure on where I'm going wrong or whether there's a much easier way to do what I'm after (I'm open to suggestions).
The function takes a parameter called command, which is a string separated by commas:
e.g. 255, 0, 0 (RED) or 0, 255, 0 (GREEN).
/*******************************************************************************
* Function Name : tinkerSetColour
* Description : Sets the strip with the appropriate colour
* Input : Pin and value
* Output : None.
* Return : 1 on success and a negative number on failure
*******************************************************************************/
int Rstart = 0, Gstart = 0, Bstart = 0;
int Rnew = 0, Gnew = 0, Bnew = 0;
int tinkerSetColour(String command)
{
sprintf(rgbString, "Rstart %i, Gstart %i, Bstart %i", Rstart, Gstart, Bstart);
Spark.publish("rgb", rgbString);
sprintf(rgbString, "Rnew %i, Gnew %i, Bnew %i", Rnew, Gnew, Bnew);
Spark.publish("rgb", rgbString);
// Clear strip.
strip.show();
int commaIndex = command.indexOf(',');
int secondCommaIndex = command.indexOf(',', commaIndex+1);
int lastCommaIndex = command.lastIndexOf(',');
int red = command.substring(0, commaIndex).toInt();
int grn = command.substring(commaIndex+1, secondCommaIndex).toInt();
int blu = command.substring(lastCommaIndex+1).toInt();
int Rend = red, Gend = grn, Bend = blu;
sprintf(rgbString, "Rend %i, Gend %i, Bend %i", Rend, Gend, Bend);
Spark.publish("rgb", rgbString);
// Larger values of 'n' will give a smoother/slower transition.
int n = 200;
for (int i = 0; i < n; i++)
{
Rnew = Rstart + (Rend - Rstart) * i / n;
Gnew = Gstart + (Gend - Gstart) * i / n;
Bnew = Bstart + (Bend - Bstart) * i / n;
// Set pixel color here.
strip.setPixelColor(0, strip.Color(Rnew, Gnew, Bnew));
}
sprintf(rgbString, "Rnew %i, Gnew %i, Bnew %i", Rnew, Gnew, Bnew);
Spark.publish("rgb", rgbString);
Rstart = red, Gstart = grn, Bstart = blu;
sprintf(rgbString, "Rstart %i, Gstart %i, Bstart %i", Rstart, Gstart, Bstart);
Spark.publish("rgb", rgbString);
return 1;
}
The problem is the colors are not fading between themselves.
Apologies if any of this is confusing. I can provide more information if necessary.
Here's the output selecting RED to begin with:
Rstart 0, Gstart 0, Bstart 0
Rnew 0, Gnew 0, Bnew 0
Rend 255, Gend 0, Bend 0
Rnew 253, Gnew 0, Bnew 0
Here's the output selecting GREEN directly afterwards:
Rstart 255, Gstart 0, Bstart 0
Rnew 253, Gnew 0, Bnew 0
Rend 0, Gend 255, Bend 0
Rnew 2, Gnew 253, Bnew 0
And then the output selecting BLUE after that:
Rstart 0, Gstart 255, Bstart 0
Rnew 2, Gnew 253, Bnew 0
Rend 0, Gend 23, Bend 255
Rnew 0, Gnew 25, Bnew 253
I've been working on the same problem for a couple days. I'm fairly new to arduino and coding so my code might be a bit simple, but for right now this seems to work.
I'm using the FastLED library.
#include "FastLED.h"
#define NUM_LEDS 15
#define DATA_PIN 6
CRGB leds[NUM_LEDS];
int fade = 2; //minutes
byte cred; //current red
byte cgreen;
byte cblue;
byte targetred; //red after fade
byte targetgreen;
byte targetblue;
byte oldred; //red before fade
byte oldgreen;
byte oldblue;
byte deltared; //difference before and after fade
byte deltagreen;
byte deltablue;
unsigned long start;
unsigned long current;
unsigned long whole;
void setup() {
FastLED.addLeds<NEOPIXEL, DATA_PIN>(leds, NUM_LEDS);
cred = 0; cblue = 0; cgreen = 0;
oldred = 0; oldblue = 0; oldgreen = 0;
update();
start = millis();
}
void loop() {
targetred = 20;
targetgreen = 220;
targetblue = 130;
deltared = targetred - oldred; deltagreen = targetgreen - oldgreen; deltablue = targetblue - oldblue;
whole = fade * 60000 + start; //fade time in milliseconds
if (cred <= targetred && millis() <= whole){
current = millis();
cred = current * deltared / whole;
cred = cred + oldred;}
if (cgreen <= targetgreen && millis() <= whole){
current = millis();
cgreen = current * deltagreen / whole;
cgreen = cgreen + oldgreen;}
if (cblue <= targetblue && millis() <= whole){
current = millis();
cblue = current * deltablue / whole;
cblue = cblue + oldblue;}
update();
}
void update(){
for (int i = 0; i <= NUM_LEDS; i++){
leds[i] = CRGB (cred, cgreen, cblue);
FastLED.show();
}};
This version doesn't actually fade to another colour afterwards, but you would just assign oldred = cred etc, then update your targetcolours. another thing is that this code is tuned to fade up due to the if cred <= target red. I originally was using != but sometimes ccolour would shoot past ctarget and keep going. I need to figure out how to set better tolerances. I am trying to avoid using delay so it will be easier later to receive outside input without lag.
Anyway, it's good to look at the same problem from different approaches, and this is another. Good luck with your fades!
I think your code is not so bad, you just need delays. Because your forloop will be too quick for you to notice the fading.
From a project I'm working on, here is a c++/pseudocode example for fading a led between to rgb colors.
It can be modified to work with your library pretty easily. The Serial.print() are for a debug purpose and can be removed once it works. Notice at the end of each loop iteration the waitMS(). you can also replace it with the Arduino delay() function.
void fade(uint16_t duration, Color startColor, Color endColor) {
int16_t redDiff = endColor.getR() - startColor.getR();
int16_t greenDiff = endColor.getG() - startColor.getG();
int16_t blueDiff = endColor.getB() - startColor.getB();
int16_t delay = 20;
int16_t steps = duration / delay;
int16_t redValue, greenValue, blueValue;
for (int16_t i = 0 ; i < steps - 1 ; ++i) {
redValue = (int16_t)startColor.getR() + (redDiff * i / steps);
greenValue = (int16_t)startColor.getG() + (greenDiff * i / steps);
blueValue = (int16_t)startColor.getB() + (blueDiff * i / steps);
Serial.print(redValue);
Serial.print("\t");
Serial.print(greenValue);
Serial.print("\t");
Serial.print(blueValue);
Serial.println("\t");
led.shine(redValue, greenValue, blueValue);
waitMs(delay);
}
led.shine(endColor);
}
Hope this helps :)
EDIT:
Here is links to the code of Color and Led:
Led Class
Color Class

Resources