Stepper Motor not rotating properly/precisely - arduino

I would like to control a tray to move back and forth automatically between several positions automatically.
Hardware: arduino nano / mega, TMC2209 and TB6600 stepper motor drivers, stepper motor 23HS84830.
Power Supply: 12V/5A
Problem: I wrote the code and noticed that although the interval between positions was fixed, the tray would move a little bit more each time therefore missing the position. At the last position it would be really off position.
Attempted Solutions: To solve this I decided to write a serial command sketch that would allow me to simulate this behaviour, define intervals, number of positions, cycles, laps, microstepping, see code below.
I had just run a sequence and made it back to the main menu. these are all the options that can be configured.
Made some trials and noticed that my sketch was outputing exactly what I configured in the right way (see picture of logic analyzer below).
In this picture the what happens is:
stepper motor rotates 5 laps (8000 steps) and waits 0,5 seconds before rotating again. repeats 5 times.
changes direction
does the same as step 1
What I did to try and solve the problem so far:
changed between TMC2209 and TB6600 to see if the problem would be the driver: it wasn't happens with both drivers
checked with the logic analyzer if the problem was the code: it wasn't, output of code is consistent and easily measurable
reviewed the connections of the stepper motor coils. not a problem and according to the motor datasheet/stepper motor driver datasheet.
At this moment, I cannot understand why the motor is not moving correctly and I would appreciate experienced support to solve this since it seems quite trivial but I cannot find the bug.
Thanks!
int PUL=4; //define Pulse pin
int DIR=3; //define Direction pin
int ENA=2; //define Enable Pin
#define left 1
#define right 0
#define LEFT 1
#define RIGHT 0
int steps_per_revolution = 200;
int minutes = 60;
long int input_value = 0;
long int _speed = 0;
long int temp_speed_rpm = 0;
long int speed_rpm = 0;
long int steps = 0;
int cycles = 0;
int positions = 0;
long int laps = 0;
long int rpm = 0;
int microstepping = 0;
long int total_laps = 0;
// Serial Commands
String command;
String inString = "";
// control flag to show the menu
boolean refresh_commands = false;
// DIRECTION LOW - MOVES RIGHT
// DIRECTION HIGH - MOVES LEFT
void setup() {
pinMode (PUL, OUTPUT);
pinMode (DIR, OUTPUT);
pinMode (ENA, OUTPUT);
Serial.begin(115200);
menu_print();
}
void loop()
{
if(Serial.available()){
command = Serial.readStringUntil('\n');
if(command.equals("p")){
variable_print();
refresh_commands = true;
}
if(command.equals("ss")){
Serial.println("0");
_speed = input_data();
Serial.print(_speed);
Serial.println(" uS");
refresh_commands = true;
}
if(command.equals("ssr")){
Serial.println("ssr");
temp_speed_rpm = input_data();
_speed = calculate_speed(temp_speed_rpm);
refresh_commands = true;
}
if(command.equals("sd")){ // checks if one direction is set, changes and then changes back again
if(digitalRead(DIR) == HIGH)
{
change_direction(right);
Serial.println("Direction changed to: RIGHT");
}
else
{
if(digitalRead(DIR) == LOW)
{
change_direction(left);
Serial.println("Direction changed to: LEFT");
}
}
refresh_commands = true;
}
if(command.equals("sst")){
Serial.println("sst");
steps = input_data();
Serial.println(" ");
Serial.print(steps);
Serial.println(" - steps configured");
refresh_commands = true;
}
if(command.equals("sm")){
Serial.println("sm");
Serial.println("sm --> how many microsteps?");
microstepping = input_data();
Serial.println(microstepping);
refresh_commands = true;
}
if(command.equals("sp")){
Serial.println("sp");
positions = input_data();
Serial.println("sp --> Configure Positions");
Serial.println(positions);
refresh_commands = true;
}
if(command.equals("sc")){
Serial.println("sc");
cycles = input_data();
Serial.println("sc --> Configure Cycles");
Serial.println(cycles);
refresh_commands = true;
}
if(command.equals("sl")){
Serial.println("sl");
Serial.println("sl --> how many laps?");
laps = input_data();
Serial.print(laps);
refresh_commands = true;
}
if(command.equals("1")){
Serial.println("1");
change_direction(left);
steps = input_data(); // Asks before for how many steps to rotate and changes the value.
rotate_motor(steps);
refresh_commands = true;
}
if(command.equals("2")){
Serial.println("2");
change_direction(right);
steps = input_data(); // Asks before for how many steps to rotate and changes the value.
rotate_motor(steps);
refresh_commands = true;
}
if(command.equals("3")){
Serial.println("3");
change_direction(left);
rotate_motor(steps);
refresh_commands = true;
}
if(command.equals("4")){
Serial.println("4");
change_direction(right);
rotate_motor(steps);
refresh_commands = true;
}
if(command.equals("rs")){
Serial.println("rs --> Run Sequence");
variable_print();
delay(3000);
total_laps = laps * steps_per_revolution * microstepping;
// run the planned sequence
run_sequence(cycles, positions);
refresh_commands = true;
}
if(command.equals("rl")){
Serial.println("rl");
Serial.println("rl --> how any laps? ");
variable_print();
delay(3000);
total_laps = laps * steps_per_revolution * microstepping;
// calculate laps and activate motor
run_laps();
refresh_commands = true;
}
else{
Serial.println("Invalid command");
refresh_commands = true;
}
refresh_commands = true; // added here to remove from all other commands
}
if (refresh_commands == true){
menu_print();
refresh_commands = false;
}
}
void run_sequence(int cycles, int positions){
int j = 0;
for(j=0; j<cycles; j++)
{
Serial.print("Cycle: ");
Serial.println(j);
// Start always moving to LEFT
change_direction(left);
Serial.println("Direction changed to: LEFT");
for(int i=0; i<positions; i++)
{
Serial.print("FWD Pos.: ");
Serial.println(i);
rotate_motor(total_laps);
delay(500);
}
delay(1000);
//change_direction(right);
change_direction(right);
Serial.println("Direction changed to: RIGHT");
delay(100);
for(int i=0; i<positions; i++)
{
Serial.print("BWD Pos.: ");
Serial.println(i);
rotate_motor(total_laps);
delay(500);
}
}
}
void run_laps(){
// long int total_laps = 0;
// total_laps = laps * steps_per_revolution * microstepping;
// moved to the main cycle and changed run_sequence to include the laps
Serial.print("Total Laps Steps: "); // to be tested.
Serial.println(total_laps);
Serial.print("Laps: "); // to be tested.
Serial.println(laps);
Serial.print("Steps/rev: "); // to be tested.
Serial.println(steps_per_revolution);
Serial.print("MicroStepping: "); // to be tested.
Serial.println(microstepping);
// change direction
change_direction(left);
// rotate motor
rotate_motor(total_laps); // variables must be long otherwise we cannot do the same number of steps as others
delay(1000);
// change direction
change_direction(right);
// rotate motor
rotate_motor(total_laps);
Serial.print(laps);
Serial.println(" - Laps completed.");
}
int input_data(){
int inChar = 0;
boolean flag = false;
Serial.println("How many?");
Serial.println(" ");
do{
while (Serial.available() > 0) {
inChar = Serial.read();
if (isDigit(inChar)) {
// convert the incoming byte to a char and add it to the string:
inString += (char)inChar;
}
// if you get a newline, print the string, then the string's value:
if (inChar == '\n') {
input_value = inString.toInt();
// clear the string for new input:
inString = "";
flag = true;
}
// // if the received char is an 'Z', then it triggers a flag to leave the menu
// if(inChar == 'Z')
// {
// flag = true;
// }
}
}while(!flag);
return input_value;
}
void rotate_motor(long int motor_steps){
Serial.print("Starting Rotation --> ");
Serial.println(motor_steps);
long int i=0;
digitalWrite(ENA, LOW);
for (i=0; i<motor_steps; i++)
{
digitalWrite(PUL,HIGH);
delayMicroseconds(_speed);
digitalWrite(PUL,LOW);
delayMicroseconds(_speed);
}
Serial.print("i --> ");
Serial.print(i);
digitalWrite(ENA, HIGH);
Serial.println("Finished rotation!");
}
void change_direction(bool direction){
delayMicroseconds(500);
digitalWrite(ENA,HIGH);
delayMicroseconds(100);
digitalWrite(DIR,direction);
delayMicroseconds(500);
digitalWrite(ENA,LOW);
delayMicroseconds(100);
Serial.print("Read Dir: ");
Serial.println(digitalRead(DIR));
}
void menu_print(){
Serial.println(" ");
Serial.println("p --> Display Parameters");
Serial.println("ss --> Set Speed (tON/tOFF)");
Serial.println("ssr --> Set Speed (RPM)");
Serial.println("sd --> Configure direction");
Serial.println("sst --> Set Steps");
Serial.println("sm --> Set MicroStepping (default = 1)");
Serial.println("sp --> Set Positions");
Serial.println("sc --> Set Cycles");
Serial.println("sl --> Set Laps");
Serial.println("1 --> Move LEFT x steps");
Serial.println("2 --> Move RIGHT x steps");
Serial.println("3 --> Move LEFT 1 position");
Serial.println("4 --> Move RIGHT 1 position");
Serial.println("rs --> Run Sequence");
Serial.println("rl --> Run Laps");
Serial.println(" ");
}
void variable_print(){
Serial.print("Speed: ");
Serial.print(_speed);
Serial.println(" us");
Serial.print("Speed: ");
Serial.print(temp_speed_rpm);
Serial.println(" RPM");
Serial.print("Steps: ");
Serial.println(steps);
Serial.print("Microstepping: ");
Serial.println(microstepping);
Serial.print("Positions: ");
Serial.println(positions);
Serial.print("Cycles: ");
Serial.println(cycles);
Serial.print("Laps: ");
Serial.println(laps);
Serial.print("Direction: ");
read_direction();
}
bool read_direction(){
//bool dir_state = 0;
if(digitalRead(DIR) == HIGH)
{
Serial.println("LEFT");
}
else
{
if(digitalRead(DIR) == LOW)
{
Serial.println("RIGHT");
}
}
return digitalRead(DIR);
}
long int calculate_speed(long int _speed){
float steps_per_second = 0; // truncating a float to int -> error chance here
float temp_speed = 0;
Serial.print("FUNCTION: Calculate_speed: ");
Serial.print(_speed);
Serial.println(" RPM");
steps_per_second = (_speed * steps_per_revolution) / minutes;
Serial.print("FUNCTION: steps_per_second: ");
Serial.println(steps_per_second);
temp_speed = (1 / steps_per_second);
temp_speed = temp_speed / 2; // to find Ton and Toff
temp_speed = temp_speed / 0.000001; // to convert to microseconds (input to delayMicroseconds() function)
temp_speed = (int) temp_speed;
// Serial.print("FUNCTION: _speed in microseconds: ");
// Serial.print(temp_speed,5);
// Serial.println(" uS");
Serial.print("FUNCTION: _speed in microseconds: ");
Serial.print(temp_speed);
Serial.println(" uS");
return temp_speed;
}

Related

Arduino script throws error "X was not declared in this scope"

Code and scheme from this link: https://www.makerspaces.com/15-simple-arduino-uno-breadboard-projects/
I'm a total beginner. I just started and I had some difficulties with the board, my PC isn't recognising the board and I wanted to create a simple circuit to test some commands, to see if I solved the problem
Full code:
// Pin assignement
#define btnPin 7
#define led1Pin 8
#define led2Pin 9
#define led3Pin 10
enum fcnMode {
OFF,
LED1,
LED2,
LED3,
FADE1,
ALL,
BLINK,
NBSTATE
}; // OFF = 0 and NBSTATE=7
int ledState1 = LOW,ledState2 = LOW,ledState3 = LOW; // ledState used to set the LED
unsigned long buttonState = 0;
int funcState=0;
unsigned long currentMillis1,currentMillis2,currentMillis3; // will store current time
unsigned long previousMillis1,previousMillis2,previousMillis3; // will store last time LED was updated
const long interval1 = 100; // interval at which to blink (milliseconds)
const long interval2 = 300;
const long interval3 = 500;
/******************************************************************\
* PRIVATE FUNCTION: setup
*
* PARAMETERS:
* ~ void
*
* RETURN:
* ~ void
*
* DESCRIPTIONS:
* Initiate inputs/outputs
*
\******************************************************************/
void setup(){
Serial.begin(9600); // initialize serial port
pinMode(btnPin,INPUT_PULLUP);
pinMode(led1Pin,OUTPUT);
pinMode(led2Pin,OUTPUT);
pinMode(led3Pin,OUTPUT);
}
/******************************************************************\
* PRIVATE FUNCTION: loop
*
* PARAMETERS:
* ~ void
*
* RETURN:
* ~ void
*
* DESCRIPTIONS:
* Main Function of the code
\******************************************************************/
void loop(){
buttonPressed();
setMode();
}
/******************************************************************
* SUBFUNCTIONS
\******************************************************************/
void buttonPressed() {
buttonState = pulseIn(btnPin,HIGH,1000000);
if (buttonState > 50){
funcState += 1;
Serial.print("Button state n: ");
Serial.println(funcState);
}
funcState=funcState%NBSTATE;
}
void setMode() {
// All Off
digitalWrite(led1Pin,LOW);
digitalWrite(led2Pin,LOW);
digitalWrite(led3Pin,LOW);
Serial.print("Function : ");
Serial.println(funcState);
switch(funcState){
case OFF:
break;
case LED1:
digitalWrite(led1Pin,HIGH);
break;
case LED2:
digitalWrite(led2Pin,HIGH);
break;
case LED3:
digitalWrite(led3Pin,HIGH);
break;
case FADE1:
fade1();
break;
case ALL:
digitalWrite(led1Pin,HIGH);
digitalWrite(led2Pin,HIGH);
digitalWrite(led3Pin,HIGH);
break;
case BLINK:
blinkLed1();
blinkLed2();
blinkLed3();
break;
}
}
void fade1(){
int brightness = 0;
int fadeAmount = 5;
for (brightness=0;brightness<=255;brightness+=fadeAmount){
analogWrite(led1Pin, brightness);
delay(30);
}
for (brightness=255;brightness>=0;brightness-=fadeAmount){
analogWrite(led1Pin, brightness);
delay(30);
}
}
void blinkLed1(){
currentMillis1 = millis();
if (currentMillis1 - previousMillis1 >= interval1) {
// save the last time you blinked the LED
previousMillis1 = currentMillis1;
// if the LED is off turn it on and vice-versa:
if (ledState1 == LOW) {
ledState1 = HIGH;
} else {
ledState1 = LOW;
}
// set the LED with the ledState of the variable:
digitalWrite(led1Pin, ledState1);
}
}
void blinkLed2(){
currentMillis2 = millis();
if (currentMillis2 - previousMillis2 >= interval2) {
// save the last time you blinked the LED
previousMillis2 = currentMillis2;
// if the LED is off turn it on and vice-versa:
if (ledState2 == LOW) {
ledState2 = HIGH;
} else {
ledState2 = LOW;
}
// set the LED with the ledState of the variable:
digitalWrite(led2Pin, ledState2);
}
}
void blinkLed3(){
currentMillis3 = millis();
if (currentMillis3 - previousMillis3 >= interval3) {
// save the last time you blinked the LED
previousMillis3 = currentMillis3;
// if the LED is off turn it on and vice-versa:
if (ledState3 == LOW) {
ledState3 = HIGH;
} else {
ledState3 = LOW;
}
// set the LED with the ledState of the variable:
digitalWrite(led3Pin, ledState3);
}
}
Error:
sketch_may09b.cpp: In function 'void setup()':
sketch_may09b:38: error: 'INPUT_PULLUP' was not declared in this scope
Your file extension is .cpp. I think it should be .ino if you are in Arduino IDE. If it doesn't help try just INPUT and add a pullup resistor to your button.

Arduino TTL jpeg Serial Camera and esp8266 wifi Wi-Fi module

I'm writing a code to take pictures if the distance is more than a certain distance after measuring the distance.
And send the distance data to Thingspeak, Store the photos on the SD card.
However, the program keeps stopping in the middle.
Serial moniter capture
Distance measurement and Thingspeak server data transfer / camera shooting were developed separately.
The two source codes worked normally independently.
But when the two codes are combined, there is an error.
Parts for Use : Arduino Uno, esp8266 wifi module, TTL Serial JPEG Camera with NTSC Video, 2Y0A21 Infrared Distance Sensor, Micro SD card adapter
#include <SoftwareSerial.h>
#include <stdlib.h>
#include <Adafruit_VC0706.h>
#include <SPI.h>
#include <SD.h>
#define DEBUG true
#define chipSelect 10
const int distancePin = 0;
String apiKey = "39R00EYW0BTKK5JJ";
SoftwareSerial esp8266(2, 3); //TX/RX
#if ARDUINO >= 100
SoftwareSerial cameraconnection = SoftwareSerial(4, 5); //TX/RX
#else
NewSoftSerial cameraconnection = NewSoftSerial(4, 5);
#endif
Adafruit_VC0706 cam = Adafruit_VC0706(&cameraconnection);
int defaultDistance = 0;
int temp = 0;
void ThingspeakSendData(String alarmData);
void Snapshots();
void setup() {
#if !defined(SOFTWARE_SPI)
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
if (chipSelect != 53) pinMode(53, OUTPUT); // SS on Mega
#else
if (chipSelect != 10) pinMode(10, OUTPUT); // SS on Uno, etc.
#endif
#endif
Serial.begin(9600);
Serial.println("VC0706 Camera snapshot test");//the program keeps stopping in the middle.
if (cam.begin()) {
Serial.println("Camera Found:");
}
else {
Serial.println("No camera found?");
return;
}
char *reply = cam.getVersion();
if (reply == 0) {
Serial.print("Failed to get version");
}
else {
Serial.println("-----------------");
Serial.print(reply);
Serial.println("-----------------");
}
cam.setImageSize(VC0706_640x480);
uint8_t imgsize = cam.getImageSize();
Serial.print("Image size: ");
if (imgsize == VC0706_640x480) Serial.println("640x480");
if (imgsize == VC0706_320x240) Serial.println("320x240");
if (imgsize == VC0706_160x120) Serial.println("160x120");
esp8266.begin(9600);
sendData("AT+RST\r\n", 2000, DEBUG); //reset module
sendData("AT+CWMODE=1\r\n", 1000, DEBUG); //dual mode로 설정
sendData("AT+CWJAP=\"0sangsiljangnim\",\"123456788\"\r\n", 5000, DEBUG);
// 2Y0A21
analogReference(DEFAULT);
pinMode(distancePin, INPUT);
// distancePin 2Y0A21
int raw = analogRead(distancePin);
int volt = map(raw, 0, 1023, 0, 5000);
int distance = (21.61 / (volt - 0.1696)) * 1000;
defaultDistance = distance;
Serial.println("Default : " + defaultDistance);
}
void loop() {
// distancePin 2Y0A21
int raw = analogRead(distancePin);
int volt = map(raw, 0, 1023, 0, 5000);
int distance = (21.61 / (volt - 0.1696)) * 1000;
Serial.println(distance);
if (distance < defaultDistance)
{
String alarmData = "1";
esp8266.listen();
ThingspeakSendData(alarmData);
cameraconnection.listen();
Snapshots();
}
else if (distance == defaultDistance)
{
String alarmData = "0";
esp8266.listen();
ThingspeakSendData(alarmData);
}
delay(3000);
}
void ThingspeakSendData(String alarmData) {
String cmd = "AT+CIPSTART=\"TCP\",\"";
cmd += "184.106.153.149";
cmd += "\",80";
esp8266.println(cmd);
if (esp8266.find("Error")) {
Serial.println("AT+CIPSTART error");
return;
}
String getStr = "GET /update?api_key=";
getStr += apiKey;
getStr += "&field1=";
getStr += String(alarmData);
getStr += "\r\n\r\n";
// Send Data
cmd = "AT+CIPSEND=";
cmd += String(getStr.length());
esp8266.println(cmd);
if (esp8266.find(">")) {
esp8266.print(getStr);
}
else {
esp8266.println("AT+CIPCLOSE");
// alert uesp8266
Serial.println("AT+CIPCLOSE");
}
}
String sendData(String command, const int timeout, boolean debug) {
String response = "";
esp8266.print(command);
long int time = millis();
while ((time + timeout) > millis()) {
while (esp8266.available()) {
char c = esp8266.read();
response += c;
}
}
if (debug) {
Serial.print(response);
}
return response;
}
void Snapshots() {
Serial.println("Snap in 3 secs...");
delay(3000);
if (!cam.takePicture())
Serial.println("Failed to snap!");
else
Serial.println("Picture taken!");
if (!SD.begin(chipSelect)) {
Serial.println("Card failed, or not present");
return;
}
char filename[13];
strcpy(filename, "IMAGE00.JPG");
for (int i = 0; i < 100; i++) {
filename[5] = '0' + i / 10;
filename[6] = '0' + i % 10;
if (!SD.exists(filename)) {
break;
}
}
File imgFile = SD.open(filename, FILE_WRITE);
uint16_t jpglen = cam.frameLength();
Serial.print("Storing ");
Serial.print(jpglen, DEC);
Serial.print(" byte image.");
int32_t time = millis();
pinMode(8, OUTPUT);
byte wCount = 0;
while (jpglen > 0) {
uint8_t *buffer;
uint8_t bytesToRead = min(32, jpglen);
buffer = cam.readPicture(bytesToRead);
imgFile.write(buffer, bytesToRead);
if (++wCount >= 64) {
Serial.print('.');
wCount = 0;
}
jpglen -= bytesToRead;
}
imgFile.close();
time = millis() - time;
Serial.println("done!");
Serial.print(time); Serial.println(" ms elapsed");
}

Problem with interruptions in Arduino Uno

I work with interruptions in Arduino UNO. In this project, I want to when the Door is opened the LED blink 10 times, and when the door is closed again, stop blinking the LED and exit the function. But in this code the LED only turn on and off once and it does not flash again.
My other problem is that, when the door is opened or closed, sometimes the opened or closed word appears several times in the Series monitor.
const byte LED_Red = 13;
const byte DOOR_SENSOR = 2; // magnetic door sensor pin
volatile int SensorState = LOW; // 0 close - 1 open wwitch
void setup()
{
Serial.begin(9600);
pinMode(LED_Red, OUTPUT);
pinMode(DOOR_SENSOR, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(DOOR_SENSOR), DoAction, CHANGE);
}
void DoAction()
{
SensorState = digitalRead(DOOR_SENSOR);
if (SensorState == HIGH) {
Serial.println("Opened");
blinkLED(10, 500);
}
else {
Serial.println("Closed");
}
}
void blinkLED(int repeats, int time)
{
for (int i = 0; i < repeats; i++) {
if (SensorState == HIGH) {
digitalWrite(LED_Red, HIGH);
delay(time);
digitalWrite(LED_Red, LOW);
delay(time);
}
else
return;
}
}
void loop()
{
}
You can't simply put a delay() on an interrupt's function. You need to just set a flag when the door is opened and based on that start blinkLED inside the main loop.
I also recommend you to use millis() function for an unblocking delay inside blinkLED function (e.g when you want to stop blinking while the door is closed).
const byte LED_Red = 13;
const byte DOOR_SENSOR = 2; // magnetic door sensor pin
// flag to check door is opened
volatile bool isOpened = false;
//flag to check already blinked
volatile bool isBlinked = false;
void setup()
{
Serial.begin(9600);
pinMode(LED_Red, OUTPUT);
pinMode(DOOR_SENSOR, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(DOOR_SENSOR), DoAction, CHANGE);
}
void DoAction()
{
if (digitalRead(DOOR_SENSOR) == HIGH)
{
//Serial.println("Opened");
isOpened = true;
}
else
{
isOpened = false;
isBlinked = false;
//Serial.println("Closed");
}
}
void blinkLED(int repeats, int time)
{
byte LEDState = LOW;
unsigned long delay_start = millis();
for (int i = 0; i < 2 * repeats; i++)
{
//Toggle LED state
if (LEDState == HIGH)
LEDState = LOW;
else
LEDState = HIGH;
// set value
digitalWrite(LED_Red, LEDState);
// some unblocking delay
while (millis() - delay_start < time)
{
// return if door is closed
if (!isOpened)
{
// turn off LED
digitalWrite(LED_Red, LOW);
return;
}
}
delay_start = millis();
}
isBlinked = true;
}
void loop()
{
// Check isBlinked beacue don't want to blink again until door is closed
if (isOpened && !isBlinked)
{
blinkLED(10, 500);
}
}

Move a BLDC motor using timer interrupt

I wanted to move my BLDC motor using a timer interrupt. Somehow, the motor would not spin at all. I want the motor to spin according to the RPM set in the code. I was wondering if you guys can pin point my mistakes. Here is the code:
#include <Wire.h> // Comes with Arduin
#define POSITIVE 1
int timerPin = 11;
int timer1_counter;
int prescaler;
// STATE CONDITION FOR MAIN LOOP
enum { enter_values, spin , finish } systemstate;
unsigned long previousMillis = 0;
// RPM MEASUREMENT
const int dataIN = 2; //IR sensor INPUT
unsigned long prevmillis; // To store time
unsigned long duration; // To store time difference
unsigned long lcdrefresh; // To store time for lcd to refresh
int rpm; // RPM value
boolean currentstate; // Current state of IR input scan
boolean prevstate; // State of IR sensor in previous scan
// DECLARE
int stage1speed , stage1time , stage2speed , stage2time , stage3speed , stage3time ;
void setup()
{
Serial.begin(9600);
systemstate = enter_values; // set up the starting state
pinMode(dataIN, INPUT);
prevmillis = 0;
prevstate = LOW;
pinMode (timerPin, OUTPUT);
//rmc timer interrupt
// initialize timer1
noInterrupts(); // disable all interrupts
TCCR1A = 0;
TCCR1B = 0;
// Set timer1_counter to the correct value for our interrupt interval
//timer1_counter = 64911; // preload timer 65536-16MHz/256/100Hz
//timer1_counter = 64286; // preload timer 65536-16MHz/256/50Hz
//timer1_counter = 34286; // preload timer 65536-16MHz/256/2Hz
timer1_counter = 65536 - F_CPU/256/2;
prescaler = 8;
TCNT1 = timer1_counter; // preload timer
TCCR1B |= (1 << CS11) | (0 << CS10); // prescaler
TIMSK1 |= (1 << TOIE1); // enable timer overflow interrupt
interrupts(); // enable all interrupts
}
ISR(TIMER1_OVF_vect) // interrupt service routine
{
static byte outp = 0;
TCNT1 = timer1_counter; // preload timer
digitalWrite(timerPin, outp);
outp = 1-outp;
}
//FUNCTION FOR KEY IN SPEED AND TIME
void enter_speed_time()
{
stage1speed = 4000;
stage1time = 10;
stage2speed = 6000;
stage2time = 10;
stage3speed = 8000;
stage3time = 10;
return;
}
// FUNCTION FOR RPM MEASUREMENT
void rpmMeasure()
{
static long last_update;
// RPM Measurement
currentstate = digitalRead(dataIN); // Read IR sensor state
if ( prevstate != currentstate) // If there is change in input
{
if ( currentstate == HIGH ) // If input only changes from LOW to HIGH
{
duration = ( micros() - prevmillis ); // Time difference between revolution in microsecond
rpm = (60000000 / duration); // rpm = (1/ time millis)*1000*1000*60;
prevmillis = micros(); // store time for next revolution calculation
}
}
prevstate = currentstate; // store this scan (prev scan) data for next scan
if (millis()-last_update > 1000)
{
Serial.print ("speed=");
Serial.println (rpm);
last_update = millis();
}
}
void set_speed (int speed)
{
Serial.print ("set speed=");
Serial.println (speed);
timer1_counter = 65536 - F_CPU/prescaler/speed*60/2;
//myservo.write(speed);
}
// FUNCTION FOR MOTOR SPINNING ACCORDING TO TIME AND SPEED
void motorspin()
{
unsigned long currentMillis = millis();
int idleValue = 0;
static enum { IDLE, STAGE1, STAGE2, STAGE3 } spinningstate;
switch (spinningstate) {
case IDLE:
set_speed(stage1speed);
previousMillis = currentMillis;
spinningstate = STAGE1;
break;
case STAGE1:
if (currentMillis - previousMillis >= stage1time * 1000) {
set_speed(stage2speed);
previousMillis = currentMillis;
spinningstate = STAGE2;
}
break;
case STAGE2:
if (currentMillis - previousMillis >= stage2time * 1000) {
set_speed(stage3speed);
previousMillis = currentMillis;
spinningstate = STAGE3;
}
break;
case STAGE3:
if (currentMillis - previousMillis >= stage3time * 1000) {
set_speed(idleValue);
spinningstate = IDLE;
}
break;
}
}
// MAIN LOOP
void loop()
{
switch (systemstate)
{
case enter_values:
enter_speed_time();
systemstate = spin;
break;
case spin:
rpmMeasure();
motorspin();
if (0) // haven't figure out yet
{
systemstate = finish;
}
break;
case finish:
systemstate = enter_values;
break;
}
}

Arduino calculating the frequency - what am I doing wrong here?

I'm a newbie when it comes to electronics and Arduino - so the best way is to just to play around with it, right?
I have started a small project that utilize and LDR (Light Density Resistor) and want to use it to calculate the frequency that a light beam is blocked or turned off.
For debugging purposes I setup a small LED that blinks at a defined frequency (5 Hz etc.) and use a LCD to display the output.
I have a problem with my top right corner... It seems as it performs wrongly. It was the intention that it should show the registered frequency, but while debugging I have set it to show the number of counts in an interval of 5 sec (5,000 msec). But it appears as 24 is the max no matter what frequency I set (When I get it to show the right number [5 sec x 5 Hz = 25] I will divide by the time interval and get the results in Hz). It also shows 24.0 for 9 Hz etc..
I also have this: YouTube video
...but some fumbling in the beginning caused the LED to move a bit so it counted wrong. But in the end it "works".. But the 24.0 keeps being constant
This is my code:
#include <LiquidCrystal.h>
LiquidCrystal lcd(7, 8, 9, 10, 11 , 12);
int booBlocked = 0;
int counter = 0;
int checkValue = counter + 1;
int ledPin = 3; // LED connected to digital pin 3
int value = LOW; // previous value of the LED
long previousMillis = 0; // will store last time LED was updated
long freqency = 5; // Hz (1/sec)
long thousand = 1000;
long interval = thousand / freqency; // milliseconds
//long interval = 59; // interval at which to blink (milliseconds)
int tValue = 0; // Threshold value used for counting (are calibrated in the beginning)
long pMillis = 0;
long inter = 5000;
int pCount = 0;
float freq = 0; // Calculated blink frequency...
void setup() {
lcd.begin(16, 2);
lcd.setCursor(0,1); lcd.print(interval);
lcd.setCursor(4,1); lcd.print("ms");
pinMode(ledPin, OUTPUT); // sets the digital pin as output
lcd.setCursor(0,0); lcd.print(freqency);
lcd.setCursor(4,0); lcd.print("Hz");
}
void loop() {
// Print LDR sensor value to the display
int sensorValue = analogRead(A0);
lcd.setCursor(7,1);
lcd.print(sensorValue);
delay(100);
if (millis() > 5000){
doCount(sensorValue);
updateFreq();
lcd.setCursor(7+5,0);
lcd.print(freq);
} else {
setThresholdValue(sensorValue);
lcd.setCursor(7+5,1);
lcd.print(tValue);
}
// LED BLINK
if (millis() - previousMillis > interval) {
previousMillis = millis(); // remember the last time we blinked the LED
// if the LED is off turn it on and vice-versa.
if (value == LOW)
value = HIGH;
else
value = LOW;
digitalWrite(ledPin, value);
}
}
void updateFreq(){
long now = millis();
long t = now - pMillis;
if (t >= 10000) {
freq = (float) (counter - pCount);
//freq = ((float) (counter - pCount)) / (float) 10.0;
pMillis = now; // remember the last time we blinked the LED
pCount = counter;
}
}
void setThresholdValue(int sensorValue){
if (sensorValue > int(tValue/0.90)){
tValue = int (sensorValue*0.90);
}
}
void doCount(int sensorValue){
// Count stuff
if (sensorValue < tValue){
booBlocked = 1;
//lcd.setCursor(0,0);
//lcd.print("Blocked");
} else {
booBlocked = 0;
//lcd.setCursor(0,0);
//lcd.print(" ");
}
if (booBlocked == 1) {
if (counter != checkValue){
counter = counter + 1;
lcd.setCursor(7,0);
lcd.print(counter);
}
} else {
if (counter == checkValue){
checkValue = checkValue + 1;
}
}
}
UPDATE
A more "clean" code (please see my own answer)
#include <LiquidCrystal.h>
// Initiate the LCD display
LiquidCrystal lcd(7, 8, 9, 10, 11 , 12); // see setup at http://lassenorfeldt.weebly.com/1/post/2013/02/ardunio-lcd.html
long updateInterval = 150; // ms
long updateTime = 0;
// Declare the pins
int ledPin = 3; // LED connected to digital pin 3
// LED setup
int value = LOW; // previous value of the LED
long previousMillis = 0; // will store last time LED was updated
long freqency = 16; // Hz (1/sec)
long thousand = 1000;
long blinkInterval = thousand / freqency; // milliseconds
//// LDR counter variables ////
// Counting vars
static int counter = 0;
int booBlocked = 0;
int checkValue = counter + 1;
// Calibration vars
long onBootCalibrationTime = 5000; // time [time] to use for calibration when the system is booted
static int threshold = 0; // Value used for counting (calibrated in the beginning)
float cutValue = 0.90; // Procent value used to allow jitting in the max signal without counting.
// Frequency vars
float freq = 0; // Calculated blink frequency...
long frequencyInterval = 5000; // time [ms]
long pMillis = 0;
int pCount = 0;
void setup() {
// Setup the pins
pinMode(ledPin, OUTPUT); // sets the digital pin as output
// display static values
lcd.begin(16, 2);
lcd.setCursor(0,0); lcd.print(freqency);
lcd.setCursor(4,0); lcd.print("Hz");
lcd.setCursor(0,1); lcd.print(blinkInterval);
lcd.setCursor(4,1); lcd.print("ms");
// Setup that allows loggin
Serial.begin(9600); // Allows to get a readout from Putty (windows 7)
}
void loop() {
long time = millis();
int sensorValue = analogRead(A0);
// Blink the LED
blinkLED(time);
// Calibrate or Count (AND calculate the frequency) via the LDR
if (time < onBootCalibrationTime){
setThresholdValue(sensorValue);
} else {
doCount(sensorValue);
updateFreq(time);
}
// Update the LCD
if (time > updateTime){
updateTime += updateInterval; // set the next time to update the LCD
// Display the sensor value
lcd.setCursor(7,1); lcd.print(sensorValue);
// Display the threshold value used to determined if blocked or not
lcd.setCursor(7+5,1); lcd.print(threshold);
// Display the count
lcd.setCursor(7,0);
lcd.print(counter);
// Display the calculated frequency
lcd.setCursor(7+5,0); lcd.print(freq);
}
}
void blinkLED(long t){
if (t - previousMillis > blinkInterval) {
previousMillis = t; // remember the last time we blinked the LED
// if the LED is off turn it on and vice-versa.
if (value == LOW)
value = HIGH;
else
value = LOW;
digitalWrite(ledPin, value);
}
}
void setThresholdValue(int sValue){
if (sValue > int(threshold/cutValue)){
threshold = int (sValue*cutValue);
}
}
void doCount(int sValue){
if (sValue < threshold){
booBlocked = 1;
} else {
booBlocked = 0;
}
if (booBlocked == 1) {
if (counter != checkValue){
counter = counter + 1;
}
} else {
if (counter == checkValue){
checkValue = checkValue + 1;
}
}
}
void updateFreq(long t){
long inter = t - pMillis;
if (inter >= frequencyInterval) {
freq = (counter - pCount) / (float) (inter/1000);
pMillis = t; // remember the last time we blinked the LED
pCount = counter;
}
}
This code does not fix my question, but is just more easy to read.
The issue with your plan is that a light density resistor is going to pick up all the ambient light around and therefore be completely environment sensitive.
Have any other project hopes? This one seems like an engineering learning experience, not a coding one.
Have you thought of motor projects? Personally I'm more into home automation, but motor projects are almost instantly rewarding.
I'd recommend to re-write your doCount() function along these lines to make things simpler and easier to grasp:
void doCount(int sensorValue){
static int previousState;
int currentState;
if ( previousState == 0 ) {
currentState = sensorValue > upperThreshold;
} else {
currentState = sensorValue > lowerThreshold;
}
if ( previousState != 0 ) {
if ( currentState == 0 ) {
counter++;
}
}
previousState = currentState;
}
Let lowerThreshold and upperThreshold be, for example, 90% and 110%, respectively, of your former tValue, and you have a hysteresis to smoothen the reaction to noisy ADC read-outs.
I think i found one of the bugs.. I was using a delay() which caused some trouble..
I cleaned up the code:
#include <LiquidCrystal.h>
// Initiate the LCD display
LiquidCrystal lcd(7, 8, 9, 10, 11 , 12); // see setup at http://lassenorfeldt.weebly.com/1/post/2013/02/ardunio-lcd.html
long updateInterval = 150; // ms
long updateTime = 0;
// Declare the pins
int ledPin = 3; // LED connected to digital pin 3
// LED setup
int value = LOW; // previous value of the LED
long previousMillis = 0; // will store last time LED was updated
long freqency = 16; // Hz (1/sec)
long thousand = 1000;
long blinkInterval = thousand / freqency; // milliseconds
//// LDR counter variables ////
// Counting vars
static int counter = 0;
int booBlocked = 0;
int checkValue = counter + 1;
// Calibration vars
long onBootCalibrationTime = 5000; // time [time] to use for calibration when the system is booted
static int threshold = 0; // Value used for counting (calibrated in the beginning)
float cutValue = 0.90; // Procent value used to allow jitting in the max signal without counting.
// Frequency vars
float freq = 0; // Calculated blink frequency...
long frequencyInterval = 5000; // time [ms]
long pMillis = 0;
int pCount = 0;
void setup() {
// Setup the pins
pinMode(ledPin, OUTPUT); // sets the digital pin as output
// display static values
lcd.begin(16, 2);
lcd.setCursor(0,0); lcd.print(freqency);
lcd.setCursor(4,0); lcd.print("Hz");
lcd.setCursor(0,1); lcd.print(blinkInterval);
lcd.setCursor(4,1); lcd.print("ms");
// Setup that allows loggin
Serial.begin(9600); // Allows to get a readout from Putty (windows 7)
}
void loop() {
long time = millis();
int sensorValue = analogRead(A0);
// Blink the LED
blinkLED(time);
// Calibrate or Count (AND calculate the frequency) via the LDR
if (time < onBootCalibrationTime){
setThresholdValue(sensorValue);
} else {
doCount(sensorValue);
updateFreq(time);
}
// Update the LCD
if (time > updateTime){
updateTime += updateInterval; // set the next time to update the LCD
// Display the sensor value
lcd.setCursor(7,1); lcd.print(sensorValue);
// Display the threshold value used to determined if blocked or not
lcd.setCursor(7+5,1); lcd.print(threshold);
// Display the count
lcd.setCursor(7,0);
lcd.print(counter);
// Display the calculated frequency
lcd.setCursor(7+5,0); lcd.print(freq);
}
}
void blinkLED(long t){
if (t - previousMillis > blinkInterval) {
previousMillis = t; // remember the last time we blinked the LED
// if the LED is off turn it on and vice-versa.
if (value == LOW)
value = HIGH;
else
value = LOW;
digitalWrite(ledPin, value);
}
}
void setThresholdValue(int sValue){
if (sValue > int(threshold/cutValue)){
threshold = int (sValue*cutValue);
}
}
void doCount(int sValue){
if (sValue < threshold){
booBlocked = 1;
} else {
booBlocked = 0;
}
if (booBlocked == 1) {
if (counter != checkValue){
counter = counter + 1;
}
} else {
if (counter == checkValue){
checkValue = checkValue + 1;
}
}
}
void updateFreq(long t){
long inter = t - pMillis;
if (inter >= frequencyInterval) {
freq = (counter - pCount) / (float) (inter/1000);
pMillis = t; // remember the last time we blinked the LED
pCount = counter;
}
}
Its not as precise as I wished.. but I believe that this is might due to the way I blink the LED.
I also discovered that float cutValue = 0.90; has an influence... lowering the bar to 0.85 decrease the calculated frequency.. ??
I changed the code completely after Albert was so kind to help me out using his awesome FreqPeriodCounter library
I also added a potentiometer to control the frequency
Here is my code:
#include <FreqPeriodCounter.h>
#include <LiquidCrystal.h>
// FrequencyCounter vars
const byte counterPin = 3; // Pin connected to the LDR
const byte counterInterrupt = 1; // = pin 3
FreqPeriodCounter counter(counterPin, micros, 0);
// LCD vars
LiquidCrystal lcd(7, 8, 9, 10, 11 , 12); // see setup at http://lassenorfeldt.weebly.com/1/post/2013/02/ardunio-lcd.html
long updateInterval = 200; // ms
long updateTime = 0;
// LED vars
int ledPin = 5; // LED connected to digital pin 3
int value = LOW; // previous value of the LED
float previousMillis = 0; // will store last time LED was updated
static float freqency; // Hz (1/sec)
static float pfreqency;
static float blinkInterval; // milliseconds
boolean logging = true; // Logging by sending to serial
// Use potentiometer to control LED frequency
int potPin = 5; // select the input pin for the potentiometer
int val = 0; // variable to store the value coming from the sensor
void setup(void){
// Setup the pins
pinMode(ledPin, OUTPUT); // sets the digital pin as output
val = analogRead(potPin);
freqency = map(val, 0, 1023, 0, 25); // Hz (1/sec)
pfreqency = freqency;
blinkInterval = 1000 / (freqency*2); // milliseconds
// LCD display static values
lcd.begin(16, 2);
lcd.setCursor(0,0); lcd.print(freqency);
lcd.setCursor(4,0); lcd.print("Hz");
lcd.setCursor(14,0); lcd.print("Hz");
lcd.setCursor(0,1); lcd.print(blinkInterval);
lcd.setCursor(4,1); lcd.print("ms");
//
attachInterrupt(counterInterrupt, counterISR, CHANGE);
// Logging
if (logging) {Serial.begin(9600);}
}
void loop(void){
// Loop vars
float time = (float) millis();
float freq = (float) counter.hertz(10)/10.0;
// Blink the LED
blinkLED(time);
if (logging) {
if(counter.ready()) Serial.println(counter.hertz(100));
}
// Update the LCD
if (time > updateTime){
updateTime += updateInterval; // set the next time to update the LCD
lcdNicePrint(7+3, 0, freq); lcd.setCursor(14,0); lcd.print("Hz");
val = analogRead(potPin);
freqency = map(val, 0, 1023, 1, 30);
if (freqency != pfreqency){
pfreqency = freqency;
blinkInterval = 1000 / (freqency*2); // milliseconds
lcdNicePrint(0,0, freqency); lcd.setCursor(4,0); lcd.print("Hz");
lcd.setCursor(0,1); lcd.print(blinkInterval);
lcd.setCursor(4,1); lcd.print("ms");
}
}
}
void lcdNicePrint(int column, int row, float value){
lcd.setCursor(column, row); lcd.print("00");
if (value < 10) {lcd.setCursor(column+1, row); lcd.print(value);}
else {lcd.setCursor(column, row); lcd.print(value);}
}
void blinkLED(long t){
if (t - previousMillis > blinkInterval) {
previousMillis = t; // remember the last time we blinked the LED
// if the LED is off turn it on and vice-versa.
if (value == LOW)
value = HIGH;
else
value = LOW;
digitalWrite(ledPin, value);
}
}
void counterISR()
{ counter.poll();
}

Resources