Transition between two Steppers with independently assigned cues - arduino

I am going to use two step motors, two easy-drivers, one arduino uno.
Each step motors have gear to rotate, front and back.
First, I'd like to assign multiple rotation cues for each steppers. such as,
int step1cue1 = 329
int step1cue2 = 582
int step1cue3 = 1038
int step1 cue4 = 1790
...
int step2cue1 = 568
int step2cue2 = 1004
int step2cue3 = 1928
int step2cue4 = 3592
...
and each stepper needs minimum 0 and maximum value, such as,
int step1maxcue = 8372
int step2maxcue = 8421
each rotation speed and acceleration need to be set and controllable as well.
I would like to use rotation speed = 0 to make the stepper stopped at the cue.
** Operation
Both steppers start from 0,
First, stepper1 rotates to randomly chosen cue from its step1cues, when it almost reaches to the chosen cue, stepper2 starts to rotate to randomly chosen cue from step2cues.(stepper1 stopped at the cue with speed=0), when step2 almost reaches to its cue, stepper1 starts to rotate to another randomly chosen step1cue (not the current cue).. (stepper2 stops with speed=0).. and repeat this transition between two steppers.
Could you please help me with this code?
I would like to use the code from this,
#include <AccelStepper.h>
// Define two steppers and the pins they will use
AccelStepper stepper1(1, 9, 8);
AccelStepper stepper2(1, 7, 6);
int pos1 = 3600;
int pos2 = 5678;
void setup()
{
stepper1.setMaxSpeed(3000);
stepper1.setAcceleration(1000);
stepper2.setMaxSpeed(2000);
stepper2.setAcceleration(800);
}
void loop()
{
if (stepper1.distanceToGo() == 0)
{
pos1 = -pos1;
stepper1.moveTo(pos1);
}
if (stepper2.distanceToGo() == 0)
{
pos2 = -pos2;
stepper2.moveTo(pos2);
}
stepper1.run();
stepper2.run();
}

I don't know the AccelStepper library, so I can be wrong, but this should do what you requested:
#include <AccelStepper.h>
// With matrices it's easier
const int step1cues[] = { 329, 582, 1038, 1790 ...};
const int step2cues[] = { 568, 1004, 1928, 3592 ...};
// Define two steppers and the pins they will use
AccelStepper stepper1(1, 9, 8);
AccelStepper stepper2(1, 7, 6);
byte movingStepper;
// Distance from position when the motor should start
#define THRESHOLD 20
void setup()
{
// A0 should be disconnected. If it is used
// change this to an unused analog pin
randomSeed(analogRead(0));
stepper1.setMaxSpeed(3000);
stepper1.setAcceleration(1000);
stepper2.setMaxSpeed(3000);
stepper2.setAcceleration(1000);
// Make sure that steppers are at zero
stepper1.setCurrentPosition(0);
stepper2.setCurrentPosition(0);
// Pretend that we were moving motor 2
// It is already at position, so first
// loop will move motor 1 to a new position
movingStepper = 2;
}
void loop()
{
if (movingStepper == 1)
{
if (abs(stepper1.distanceToGo()) < THRESHOLD)
{
int nextpos;
do
{
nextpos = step2cues[random(sizeof(step2cues)/sizeof(int))];
} while (abs(nextpos - stepper2.targetPosition()) < THRESHOLD);
stepper2.moveTo(nextpos);
movingStepper = 2;
}
}
else
{
if (abs(stepper2.distanceToGo()) < THRESHOLD)
{
int nextpos;
do
{
nextpos = step1cues[random(sizeof(step1cues)/sizeof(int))];
} while (abs(nextpos - stepper1.targetPosition()) < THRESHOLD);
stepper1.moveTo(nextpos);
movingStepper = 1;
}
}
stepper1.run();
stepper2.run();
}
THRESHOLD is the "almost" in your sentence: when it is nearer than this value it will start moving the other motor. Note: the cues should be mode distant one from the other than twice the threshold value, so you won't have mixed conditions (I mean with that threshold the minimum distance from one cue and ANY other should be 40).
And by the way, you don't need to set speed to 0: when the stepper reaches the position it will halt by itself.
EDIT:
The OP asked three more features (print the next cue when chosen, print on serial the current position, set a switch when the motor reaches the cue), so here they are.
First of all I'd separate the second and third feature from the previous code, for clarity reasons. I chose to run that code only when the position changes, but this can easily be changed to
print that code only after a fixed amount of change (e.g. only after a change of 50 or more)
print that code at fixed amount of time (e.g. 0.5 s)
Anyway, here is the code:
int currentPos1 = -1;
int currentPos2 = -1;
void loop()
{
if (movingStepper == 1)
{
if (abs(stepper1.distanceToGo()) < THRESHOLD)
{
int nextpos;
do
{
nextpos = step2cues[random(sizeof(step2cues)/sizeof(int))];
} while (abs(nextpos - stepper2.targetPosition()) < THRESHOLD);
stepper2.moveTo(nextpos);
Serial.print("Next cue for motor 2: ");
Serial.println(nextpos);
movingStepper = 2;
}
}
else
{
if (abs(stepper2.distanceToGo()) < THRESHOLD)
{
int nextpos;
do
{
nextpos = step1cues[random(sizeof(step1cues)/sizeof(int))];
} while (abs(nextpos - stepper1.targetPosition()) < THRESHOLD);
stepper1.moveTo(nextpos);
Serial.print("Next cue for motor 1: ");
Serial.println(nextpos);
movingStepper = 1;
}
}
if (currentPos1 != stepper1.currentPosition())
{ // Stepper 1 has moved
currentPos1 = stepper1.currentPosition()
if (stepper1.distanceToGo() == 0)
{ // Stepper 1 has reached the final position
digitalWrite(switch1, HIGH);
}
else
{
digitalWrite(switch1, LOW);
}
Serial.print("Motor 1 pos: ");
Serial.println(currentPos1);
}
if (currentPos2 != stepper2.currentPosition())
{ // Stepper 2 has moved
currentPos2 = stepper2.currentPosition()
if (stepper2.distanceToGo() == 0)
{ // Stepper 2 has reached the final position
digitalWrite(switch2, HIGH);
}
else
{
digitalWrite(switch2, LOW);
}
Serial.print("Motor 2 pos: ");
Serial.println(currentPos2);
}
stepper1.run();
stepper2.run();
}
If you choose to modify it in order not to receive frequent motor updates (e.g. by showing the message every half a second) you should move the Stepper X has reached the final position block outside the control, so that it can be executed every iteration:
unsigned long lastShownMessages = 0;
void loop()
{
if (movingStepper == 1)
{
if (abs(stepper1.distanceToGo()) < THRESHOLD)
{
int nextpos;
do
{
nextpos = step2cues[random(sizeof(step2cues)/sizeof(int))];
} while (abs(nextpos - stepper2.targetPosition()) < THRESHOLD);
stepper2.moveTo(nextpos);
Serial.print("Next cue for motor 2: ");
Serial.println(nextpos);
movingStepper = 2;
}
}
else
{
if (abs(stepper2.distanceToGo()) < THRESHOLD)
{
int nextpos;
do
{
nextpos = step1cues[random(sizeof(step1cues)/sizeof(int))];
} while (abs(nextpos - stepper1.targetPosition()) < THRESHOLD);
stepper1.moveTo(nextpos);
Serial.print("Next cue for motor 1: ");
Serial.println(nextpos);
movingStepper = 1;
}
}
if ((millis() - lastShownMessages) > 500)
{ // More than 500 ms passed since last update:
Serial.print("Motor 1 pos: ");
Serial.println(stepper1.currentPosition());
Serial.print("Motor 2 pos: ");
Serial.println(stepper2.currentPosition());
lastShownMessages += 500;
}
if (stepper1.distanceToGo() == 0)
{ // Stepper 1 has reached the final position
digitalWrite(switch1, HIGH);
}
else
{
digitalWrite(switch1, LOW);
}
if (stepper2.distanceToGo() == 0)
{ // Stepper 2 has reached the final position
digitalWrite(switch2, HIGH);
}
else
{
digitalWrite(switch2, LOW);
}
stepper1.run();
stepper2.run();
}

PLUS, for a calibration purpose, can I see the rotation value (increment) of the stepper on the Server Monitor ? I can set the speed really low and let it roll, then I could know cue values for the stepcues that I need to set.
#include <AccelStepper.h>
// With matrices it's easier
const int step1cues[] = {10000 , 20000, 30000 , 40000 , 50000, 60000, 70000, 80000};
const int step2cues[] = {10000 , 20000, 30000 , 40000 , 50000, 60000, 70000, 80000};
// Define two steppers and the pins they will use
AccelStepper stepper1(1, 4, 3);
AccelStepper stepper2(1, 7, 6);
int switch1 = 12;
int switch2 = 13;
byte movingStepper;
// Distance from position when the motor should start
#define THRESHOLD 20
void setup()
{
Serial.begin(9600);
pinMode(switch1, OUTPUT);
pinMode(switch2, OUTPUT);
// A0 should be disconnected. If it is used
// change this to an unused analog pin
randomSeed(analogRead(0));
stepper1.setMaxSpeed(3000);
stepper1.setAcceleration(1000);
stepper2.setMaxSpeed(3000);
stepper2.setAcceleration(1000);
// Make sure that steppers are at zero
stepper1.setCurrentPosition(0);
stepper2.setCurrentPosition(0);
// Pretend that we were moving motor 2
// It is already at position, so first
// loop will move motor 1 to a new position
movingStepper = 2;
}
void loop()
{
if (movingStepper == 1)
{
if (abs(stepper1.distanceToGo()) < THRESHOLD)
{
int nextpos;
do
{
nextpos = step2cues[random(sizeof(step2cues)/sizeof(int))];
} while (abs(nextpos - stepper2.targetPosition()) < THRESHOLD);
stepper2.moveTo(nextpos);
Serial.println("2");
Serial.println(nextpos);
movingStepper = 2;
if ( nextpos = stepper2.targetPosition() ){
digitalWrite(switch1, HIGH);
}
else {
digitalWrite(switch1, LOW);
}
}
}
else
{
if (abs(stepper2.distanceToGo()) < THRESHOLD)
{
int nextpos;
do
{
nextpos = step1cues[random(sizeof(step1cues)/sizeof(int))];
} while (abs(nextpos - stepper1.targetPosition()) < THRESHOLD);
stepper1.moveTo(nextpos);
Serial.println("1");
Serial.println(nextpos);
movingStepper = 1;
if ( nextpos = stepper1.targetPosition() ){
digitalWrite(switch2, HIGH);
}
else {
digitalWrite(switch2, LOW);
}
}
}
stepper1.run();
stepper2.run();
}

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.

Why can't X be used as a function?

Sorry for the messy codebase. I am new to C++.
I'm trying to tie a loop and function together. (1) A moisture sensor and (2) a servo to turn a lever on and off based on moisture.
I'm receiving an error that 'servo' cannot be used as a function. I've tried changing servo to some other name. I'm not using servo anywhere else, such as a variable, so that doesn't seem like the issue.
Does anyone have any other advice?
#include "Servo.h"
Servo myservo; // create servo object to control a servo
int pos = 0; // servo position variable
#define SensorPin A0
float sensorValue = 0;
bool lever = 0; // soil moisture sensor variable; 0 = dry; 1 = wet;
bool pump = 0; // pump is set to 0, which = off
// servo function
void servo(bool);
void setup() {
Serial.begin(9600); // soil moisture sensor setup
myservo.attach(9); // servo setup
}
// soil moisture sensor loop
void loop() {
for (int i = 0; i <= 100; i++) {
sensorValue = sensorValue + analogRead(SensorPin);
delay(20);
}
sensorValue = sensorValue/100.0;
Serial.print("Moisture level: ");
Serial.print(sensorValue);
Serial.print(";");
if(sensorValue > 1000) {
lever = 0; // dry
}
if(sensorValue < 1000) {
lever = 1; // wet
}
switchOnOff(lever);
delay(1000);
}
// define the servo function
void switchOnOff(bool lever) {
Serial.print(" Lever: ");
Serial.print(lever);
Serial.print(";");
Serial.print(" Pump: ");
Serial.print(pump);
Serial.println(";");
if(lever == 0 && pump == 0) { // it is dry and pump is off, pump needs to be turned on
for (pos = 90; pos <= 180; pos++) { // goes from 0 degrees to 90 degrees
// in steps of 1 degree
myservo.write(pos); // tell servo to go to position in variable 'pos'
delay(15);
}
delay(4000);
for (pos = 180; pos >= 90; pos--) { // goes from 90 degrees to 0 degrees
// in steps of 1 degree
myservo.write(pos); // tell servo to go to position in variable 'pos'
delay(15);
}
pump = 1; // noting that pump is on
}
if(lever == 0 && pump == 1) { // it is dry and pump is on
return; // do nothing, return
}
if(lever == 1 && pump == 1) { // it is wet and pump is on, pump needs to be turned off
for (pos = 90; pos <= 180; pos++) { // goes from 90 degrees to 0 degrees
// in steps of 1 degree
myservo.write(pos); // tell servo to go to position in variable 'pos'
delay(15);
}
delay(4000);
for (pos = 180; pos >= 90; pos--) { // goes from 0 degrees to 90 degrees
// in steps of 1 degree
myservo.write(pos); // tell servo to go to position in variable 'pos'
delay(15);
}
pump = 0; // noting that pump is off
}
if(lever == 1 && pump == 0) { // it is wet and pump is off
return; // do nothing, return
}
}
Thank you.
The problem is in that you try to use the servo() function before properly declaring it. First, you need to declare a function before it can be mentioned in your code. Second, your definition for the servo() is incorrect. The int servo(lever), where lever is not a type. You should look toward the following: int servo(bool lever);, where lever is a parameter of type bool which the servo() function takes.
Then, your function does not return anything, it only has some "side-effects" such as myservo.write(pos);. So, it should be void.
Try the following arrangement:
#include "Servo.h"
Servo myservo; // create servo object to control a servo
// twelve servo objects can be created on most boards
// Declare the function
void servo(bool);
int pos = 0; // variable to store the servo position
#define SensorPin A0
float sensorValue = 0;
bool lever = 0; // lever is global variable of type bool
void setup() {
myservo.attach(9); // attaches the servo on pin 9 to the servo object
Serial.begin(9600);
}
void loop() {
for (int i = 0; i <= 100; i++) {
sensorValue = sensorValue + analogRead(SensorPin);
delay(1);
}
sensorValue = sensorValue/100.0;
Serial.println(sensorValue);
delay(30);
if(analogRead(0 > 100)) {
lever = 1;
}
if(analogRead(0 < 100)) {
lever = 0;
}
servo(lever);
}
// Define the function
void servo(bool lever) {
if(lever == 1) { // On
for (pos = 0; pos <= 180; pos += 1) { // goes from 0 degrees to 180 degrees
// in steps of 1 degree
myservo.write(pos); // tell servo to go to position in variable 'pos'
}
}
if(lever == 0) { // Off
for (pos = 180; pos >= 0; pos -= 1) { // goes from 180 degrees to 0 degrees
myservo.write(pos); // tell servo to go to position in variable 'pos'
}
}
}
P.S. There was also a mess with setup() being redefined here and there.

Arduino loop stops after for loop in called function

I am trying to make a radio controlled car which reacts to the frequency of music. The Loop works as it should and it gives me the correct frequency. The function berekenGrootte works too. This gives me the size of the frequency (max frequency - min frequency). But as soon as I want to use the function verdeel() which divides the size of the frequency in 10 to make the car move smoother it does it one or two times and then I don't get any feedback anymore in my serial monitor. Do I use too much Serial.print or do I need to restart the loop after the for loop in the second function?
#include <FreqMeasure.h>
void setup()
{
Serial.begin(57600);
pinMode(3, OUTPUT);
pinMode(5, OUTPUT);
pinMode(2, OUTPUT);
pinMode(4, OUTPUT);
FreqMeasure.begin();
}
double sum = 0;
int count = 0;
float lowest = 50;
float highest = 0;
float grootte = 50;
float b[9][1];
void loop()
{
if (FreqMeasure.available()) {
// average several reading together
sum = sum + FreqMeasure.read();
count = count + 1;
if (count > 30) {
float frequency = FreqMeasure.countToFrequency(sum / count);
Serial.println(frequency);
sum = 0;
count = 0;
if (frequency < lowest) {
lowest = frequency;
Serial.print("new lowest: ");
Serial.println(lowest);
berekenGrootte();
}
else if (frequency > highest) {
highest = frequency;
Serial.print("new highest: ");
Serial.println(highest);
berekenGrootte();
}
/*if(frequency > 1000){
digitalWrite(3,HIGH);
delay(1000);
digitalWrite(3,LOW);
}
else{
digitalWrite(5,HIGH);
delay(1000);
digitalWrite(5,LOW);
}*/
}
}
}
void berekenGrootte()
{
grootte = highest - lowest;
Serial.print("new grootte: ");
Serial.println(grootte);
verdeel();
}
void verdeel()
{
float plength = grootte / 10;
b[0][0] = lowest;
b[0][1] = lowest + plength;
Serial.print("low: ");
Serial.println(b[0][0]);
Serial.print("high: ");
Serial.println(b[0][1]);
float startvalue = lowest + plength;
for (int i = 1; i < 10; i++) {
b[i][0] = startvalue;
b[i][1] = startvalue + plength;
startvalue = startvalue + plength;
Serial.print(i);
Serial.print(" low: ");
Serial.println(b[i][0]);
Serial.print(i);
Serial.print(" high: ");
Serial.println(b[i][1]);
}
}
You're overstepping the bounds of your b array.
float b[9][1];
The array is only 9 * 1.
void verdeel()
{
float plength = grootte / 10;
b[0][0] = lowest;
b[0][1] = lowest + plength;
So there is no element b[0][1]. The second number can only go up to 0 since the array is size 1 in that dimension. An array of size 1 is pretty useless as an array.
You have the same problem here in this loop at the end of that function:
for (int i = 1; i < 10; i++) {
b[i][0] = startvalue;
b[i][1] = startvalue + plength;
This for loop allows i to go to 9 which is too large for the array.

Arduino LED Controller hanging at startup

I'm trying to code an LED controller that controls the intensity via PWM. However, my issue is that I can't even get to the loop portion, it seems to hang at when I declare my class. I've tried checking to see if any of my functions in my class are causing the issues, but since I can't even get to loop, there must be something wrong within the class. I've written the class and placed it into a library called LED.
The code is somewhat long, but here it is:
#ifndef LED_H
#define LED_H
#include <LiquidCrystal.h>
#include <Button.h>
#include <EEPROM.h>
#include <TimeLib.h>
#include <PWM.h>
class LED
{
public:
LED();
int read_encoder(); //Reads rotary encoder
void clearLCD();
void setAllLed();
void printLCD();
void setOneLed(int);
int setLed(int, // current time in minutes
int, // pin for this channel of LEDs
int, // start time for this channel of LEDs
int, // photoperiod for this channel of LEDs
int, // fade duration for this channel of LEDs
int, // max value for this channel
bool // true if the channel is inverted
);
void menuWizard();
int subMenuWizard(int, int, bool, bool);
void displayMainMenu();
void printMins(int, bool);
void printHMS(byte,byte,byte);
long EEPROMReadlong(long);
void EEPROMWritelong(int, long);
bool pressSelect();
bool pressBack();
void rotateCheck(int&, int, int);
//variables for the LED channels
int minCounter = 0; // counter that resets at midnight.
int oldMinCounter = 0; // counter that resets at midnight.
int ledPins[5]={2,3,5,6,7};
int ledVal[5]={0,0,0,0,0};
// Variables making use of EEPROM memory:
int variablesList[20];
bool invertedLEDs[5]={false,false,false,false,false};
//Backlight Variables
unsigned long backlightIdleMs = 0;
private:
};
#endif // LED_H
And here is the .cpp file:
#define LCD_RS 35 // RS pin
#define LCD_ENABLE 34 // enable pin
#define LCD_DATA4 33 // d4 pin
#define LCD_DATA5 32 // d5 pin
#define LCD_DATA6 31 // d6 pin
#define LCD_DATA7 30 // d7 pin
#define LCD_BACKLIGHT 9 // backlight pin
// Backlight config
#define BACKLIGHT_DIM 10 // PWM value for backlight at idle
#define BACKLIGHT_ON 70 // PWM value for backlight when on
#define BACKLIGHT_IDLE_MS 10000 // Backlight idle delay
#define ENC_A 14
#define ENC_B 15
#define ENC_PORT PINC
#include <LiquidCrystal.h>
#include <Button.h>
#include <EEPROM.h>
#include <TimeLib.h>
#include <PWM.h>
#include "LED.h"
LiquidCrystal lcd(LCD_RS, LCD_ENABLE, LCD_DATA4, LCD_DATA5, LCD_DATA6, LCD_DATA7);
Button goBack=Button(12, PULLDOWN);
Button select=Button(13, PULLDOWN);
LED::LED()
{
InitTimersSafe();
pinMode(LCD_BACKLIGHT, OUTPUT);
lcd.begin(16, 2);
digitalWrite(LCD_BACKLIGHT, HIGH);
lcd.print("sEx LED, V1");
clearLCD();
delay(5000);
analogWrite(LCD_BACKLIGHT, BACKLIGHT_DIM);
if (variablesList[0] > 1440 || variablesList[0] < 0) {
variablesList[0] = 720; // minute to start this channel.
variablesList[1] = 400; // photoperiod in minutes for this channel.
variablesList[2] = 100; // max intensity for this channel, as a percentage
variablesList[3] = 100; // duration of the fade on and off for sunrise and sunset for
// this channel.
variablesList[4] = 720;
variablesList[5] = 400;
variablesList[6] = 100;
variablesList[7] = 100;
variablesList[8] = 720;
variablesList[9] = 400;
variablesList[10] = 100;
variablesList[11] = 100;
variablesList[12] = 720;
variablesList[13] = 400;
variablesList[14] = 100;
variablesList[15] = 100;
variablesList[16] = 720;
variablesList[17] = 400;
variablesList[18] = 100;
variablesList[19] = 100;
}
else {
variablesList[0] = EEPROMReadlong(0); // minute to start this channel.
variablesList[1] = EEPROMReadlong(4); // photoperiod in minutes for this channel.
variablesList[2] = EEPROMReadlong(8); // max intensity for this channel, as a percentage
variablesList[3] = EEPROMReadlong(12); // duration of the fade on and off for sunrise and sunset for
// this channel.
variablesList[4] = EEPROMReadlong(16);
variablesList[5] = EEPROMReadlong(20);
variablesList[6] = EEPROMReadlong(24);
variablesList[7] = EEPROMReadlong(28);
variablesList[8] = EEPROMReadlong(32);
variablesList[9] = EEPROMReadlong(36);
variablesList[10] = EEPROMReadlong(40);
variablesList[11] = EEPROMReadlong(44);
variablesList[12] = EEPROMReadlong(48);
variablesList[13] = EEPROMReadlong(52);
variablesList[14] = EEPROMReadlong(56);
variablesList[15] = EEPROMReadlong(60);
variablesList[16] = EEPROMReadlong(64);
variablesList[17] = EEPROMReadlong(68);
variablesList[18] = EEPROMReadlong(72);
variablesList[19] = EEPROMReadlong(76);
}
}
void LED::printLCD(){lcd.print("test");clearLCD();delay(2000);lcd.print("testing");clearLCD();}
bool LED::pressSelect(){
if (select.uniquePress()){return 1;}
else {return 0;}
}
bool LED::pressBack(){
if (goBack.uniquePress()){return 1;}
else {return 0;}
}
void LED::clearLCD(){
lcd.clear();
}
void LED::displayMainMenu(){
oldMinCounter = minCounter;
minCounter = hour() * 60 + minute();
for (int i=0;i<17;i=i+4){
if (variablesList[i+3] > variablesList[i+1] / 2 && variablesList[i+1] > 0) {
variablesList[i+3] = variablesList[i+1] / 2;
}
if (variablesList[i+3] < 1) {
variablesList[i+3] = 1;
}
}
//check & set any time functions
if (minCounter > oldMinCounter) {
lcd.clear();
}
lcd.setCursor(0, 0);
printHMS(hour(), minute(), second());
lcd.setCursor(0, 1);
lcd.print(ledVal[0]);
lcd.setCursor(4, 1);
lcd.print(ledVal[1]);
lcd.setCursor(8, 1);
lcd.print(ledVal[2]);
}
int LED::read_encoder()
{
static int enc_states[] = {0,-1,1,0,1,0,0,-1,-1,0,0,1,0,1,-1,0};
static int old_AB = 0;
/**/
old_AB <<= 2; //remember previous state
old_AB |= ( ENC_PORT & 0x03 ); //add current state
return ( enc_states[( old_AB & 0x0f )]);
}
int LED::setLed(int mins, // current time in minutes
int ledPin, // pin for this channel of LEDs
int start, // start time for this channel of LEDs
int period, // photoperiod for this channel of LEDs
int fade, // fade duration for this channel of LEDs
int ledMax, // max value for this channel
bool inverted // true if the channel is inverted
) {
int val = 0;
//fade up
if (mins > start || mins <= start + fade) {
val = map(mins - start, 0, fade, 0, ledMax);
}
//fade down
if (mins > start + period - fade && mins <= start + period) {
val = map(mins - (start + period - fade), 0, fade, ledMax, 0);
}
//off or post-midnight run.
if (mins <= start || mins > start + period) {
if ((start + period) % 1440 < start && (start + period) % 1440 > mins )
{
val = map((start + period - mins) % 1440, 0, fade, 0, ledMax);
}
else
val = 0;
}
if (val > ledMax) {
val = ledMax;
}
if (val < 0) {
val = 0;
}
if (inverted) {
pwmWrite(ledPin, map(val, 0, 100, 255, 0));
}
else {
pwmWrite(ledPin, map(val, 0, 100, 0, 255));
}
return val;
}
void LED::printMins(int mins, //time in minutes to print
bool ampm //print am/pm?
) {
int hr = (mins % 1440) / 60;
int mn = mins % 60;
if (hr < 10) {
lcd.print(" ");
}
lcd.print(hr);
lcd.print(":");
if (mn < 10) {
lcd.print("0");
}
lcd.print(mn);
}
void LED::printHMS (byte hr,
byte mn,
byte sec //time to print
)
{
if (hr < 10) {
lcd.print(" ");
}
lcd.print(hr, DEC);
lcd.print(":");
if (mn < 10) {
lcd.print("0");
}
lcd.print(mn, DEC);
lcd.print(":");
if (sec < 10) {
lcd.print("0");
}
lcd.print(sec, DEC);
}
//EEPROM write functions
long LED::EEPROMReadlong(long address)
{
//Read the 4 bytes from the eeprom memory.
long four = EEPROM.read(address);
long three = EEPROM.read(address + 1);
long two = EEPROM.read(address + 2);
long one = EEPROM.read(address + 3);
//Return the recomposed long by using bitshift.
return ((four << 0) & 0xFF) + ((three << 8) & 0xFFFF) + ((two << 16) & 0xFFFFFF) + ((one << 24) & 0xFFFFFFFF);
}
void LED::EEPROMWritelong(int address, long value)
{
//Decomposition from a long to 4 bytes by using bitshift.
//One = Most significant -> Four = Least significant byte
byte four = (value & 0xFF);
byte three = ((value >> 8) & 0xFF);
byte two = ((value >> 16) & 0xFF);
byte one = ((value >> 24) & 0xFF);
//Write the 4 bytes into the eeprom memory.
EEPROM.write(address, four);
EEPROM.write(address + 1, three);
EEPROM.write(address + 2, two);
EEPROM.write(address + 3, one);
}
void LED::setAllLed(){
int j=0;
for (int i=0;i<17;i=i+4){
int a=i;int b=i+1;int c=i+2;int d=i+3;
ledVal[j] = setLed(minCounter, ledPins[j], variablesList[a], variablesList[b], variablesList[c], variablesList[d], invertedLEDs[j]);
j++;
}
}
void LED::setOneLed(int channel){
int j=channel;
int i=0;
if(channel==1){i+=4;}
if(channel==2){i+=8;}
if(channel==3){i+=12;}
if(channel==4){i+=16;}
int a=i;int b=i+1;int c=i+2;int d=i+3;
ledVal[j] = setLed(minCounter, ledPins[j], variablesList[a], variablesList[b], variablesList[c], variablesList[d], invertedLEDs[j]);
}
void LED::rotateCheck(int& menuCount, int minMenu, int maxMenu){
while (menuCount!=0){
int rotateCount;
rotateCount=read_encoder();
if (rotateCount) {
menuCount+=rotateCount;
if (menuCount<minMenu){menuCount==maxMenu;}
if (menuCount>maxMenu){menuCount==minMenu;}
clearLCD();
}
}
}
void LED::menuWizard(){
int menuCount=1;
String menuList[6]={"Time","LED Max","LED Start","LED End","Fade Length","Ch Override"};
String channelList[5]={"1","2","3","4","5"};
while (menuCount!=0){
rotateCheck(menuCount,1,6);
lcd.setCursor(0, 0);
lcd.print(menuList[menuCount-1]);
clearLCD();
if (goBack.isPressed()){
menuCount=0;
}
if (pressSelect() && menuCount!=0){
int timeMode=1;
int channelCount=0;
bool goBack=0;
while (goBack!=1){
if (menuCount==1){
if (pressSelect()){
timeMode++;
if (timeMode>2){timeMode=1;}
}
int timeAdjDetect=read_encoder();
if (timeMode==1){
if (timeAdjDetect){
if (timeAdjDetect>0){adjustTime(SECS_PER_HOUR);}
if (timeAdjDetect<0) {adjustTime(-SECS_PER_HOUR);}
}
lcd.setCursor(0, 0);
lcd.print("Set Time: Hrs");
lcd.setCursor(0, 1);
printHMS(hour(), minute(), second());
}
else{
if (timeAdjDetect){
if (timeAdjDetect>0){adjustTime(SECS_PER_MIN);}
if (timeAdjDetect<0) {adjustTime(-SECS_PER_MIN);}
}
lcd.setCursor(0, 0);
lcd.print("Set Time: Mins");
lcd.setCursor(0, 1);
printHMS(hour(), minute(), second());
}
clearLCD();
}
else{
rotateCheck(channelCount,0,4);
lcd.setCursor(0,0);
lcd.print("Select Channel");
lcd.setCursor(0,1);
lcd.print(channelList[channelCount]);
clearLCD();
if (pressSelect()){
if (menuCount==2){
subMenuWizard(2,channelCount,0,0);
}
if (menuCount==3){
subMenuWizard(0,channelCount,1,0);
}
if (menuCount==4){
subMenuWizard(1,channelCount,1,1);
}
if (menuCount==5){
subMenuWizard(3,channelCount,1,0);
}
}
}
if (pressBack()){goBack=1;}
}
}
}
for (int i=0;i<20;i++){
int j=0;
EEPROMWritelong(j, variablesList[i]);
j+=4;
}
}
int LED::subMenuWizard(int i, int channel, bool time, bool truetime){
if (channel==1){i=i+4;}
if (channel==2){i=i+8;}
if (channel==3){i=i+12;}
if (channel==4){i=i+16;}
while (!pressBack()){
if (time==0){
rotateCheck(variablesList[i],0,100);
lcd.setCursor(0,0);
lcd.print("Set:");
lcd.setCursor(0,1);
lcd.print(variablesList[i]);
setOneLed(channel);
clearLCD();
}
else{
if (truetime){
rotateCheck(variablesList[i],0,1439);
lcd.setCursor(0,0);
lcd.print("Set:");
lcd.setCursor(0,1);
printMins(variablesList[i] + variablesList[i-1], true);
clearLCD();
}
else {
rotateCheck(variablesList[i],0,1439);
lcd.setCursor(0,0);
lcd.print("Set:");
lcd.setCursor(0,1);
printMins(variablesList[i], true);
clearLCD();
}
setOneLed(channel);
}
}
}
and finally, the .ino file:
#define LCD_BACKLIGHT 9 // backlight pin
#define BACKLIGHT_DIM 10 // PWM value for backlight at idle
#define BACKLIGHT_ON 70 // PWM value for backlight when on
#define BACKLIGHT_IDLE_MS 10000 // Backlight idle delay
#include <LED.h>
//Initialize buttons
int buttonCount = 1;
LED main;
void setup() {
};
void loop() {
/* main.setAllLed();
//turn the backlight off and reset the menu if the idle time has elapsed
if (main.backlightIdleMs + BACKLIGHT_IDLE_MS < millis() && main.backlightIdleMs > 0 ) {
analogWrite(LCD_BACKLIGHT, BACKLIGHT_DIM);
main.clearLCD();
main.backlightIdleMs = 0;
}
if (buttonCount == 1) {
main.displayMainMenu();
}
if (buttonCount == 2) {
main.menuWizard();
buttonCount = 1;
}
*/
main.printLCD();
};
Also, in the loop portion, I've commented the part of code that is intended to run, and I'm running a function that tests to see if I've successfully entered the loop by printing "test" on screen.
I'm using a Mega for this.
LED::LED()
{
InitTimersSafe();
pinMode(LCD_BACKLIGHT, OUTPUT);
lcd.begin(16, 2);
digitalWrite(LCD_BACKLIGHT, HIGH);
lcd.print("sEx LED, V1");
clearLCD();
delay(5000);
analogWrite(LCD_BACKLIGHT, BACKLIGHT_DIM);
You have to understand that this constructor is running when the object is created and that is probably before init() is run from main. So the hardware isn't ready at that point and pinMode and digitalWrite and stuff isn't going to work. The lcd code can't really work there and I bet that is the part that is hanging things.
A constructor should only do things like initialize variables. Any code that relies on the hardware should go into a begin() or init() or whatever method that you can call from setup once it is safe to do those things. The Serial object is a great example of another class that has to do this.

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);
}

Resources