Can't get past for/while loop in Arduino - c++

New to Arduino, I have tried to make a for or a while loop to do a delay, instead of the delay() function. Have tried a LOT of values but the LED remains HIGH, it works if I use the delay() function. Note that I'm not going to use this code as delay, I just tried it and now I can't understand what goes wrong.
Board is a Nano Every, I use Arduino IDE, Fcpu = 8MHz.
const byte ledPin = 13;
byte ledState = HIGH;
pinMode(ledPin, OUTPUT);
void loop() {
unsigned long i = 0;
// read the state of the switch into a local variable:
//enaState = digitalRead(sw1);
//dirState = digitalRead(sw2);
while (i < 10000000)
{
i++;
}
//delay(1000);
ledState ^= 1;
digitalWrite(ledPin, ledState);
i = 0;
}

Adding volatile to avoid being removed by optimization works.
volatile unsigned long i = 0;

The arduino compiler by default runs with all optimizations enabled, to reduce code size and improve speed. Since at least with the Arduino IDE, source-line debugging is not possible on the Arduino itself, this normally doesn't make a visible difference for development. The above code snippet is a rare example where it does.
How to disable optimization, if one really wants to do it, is described in this question: VSCode disabling Arduino compilation optimizations for debugging (thanks #dmaxime).

Related

Arduino Nano - Why are my pins behaving the way they are?

I have a device that measures radiation, using an Elegoo Uno (knockoff Arduino brand) and an RM-60 Radiation Monitor from Aware Electronics. I have had this working for almost a year and a half as part of a high-altitude balloon payload item at my university. I am currently revisiting it, as now I want to understand and cleanup my code.
The setup goes like this:
The RM-60 has four wires. Yellow and black go to ground, red to my 5v, and the green goes to my output (More documentation can be found online).
I have a pin attached to digital 2. I had read online that pins 2 & 3 can use attachInterrputs for the Uno. But for whatever reason, setting my pinMode() to 2 won't work. I have found that when I set my pin to 8, with my actual wire connecting to digital 2, I can read it fine.
This is what I am confused about. I felt that after learning how these inputs work, I am doing it right. But it's not working. So then why, when my pin set to 8, is the device running correctly? If I am doing this wrong (or inefficiently), what tips or pointers can you give me on how to optimize/repair this?
I have my previous code, compiled and tested from almost two years ago. It works as is, but I just don't understand why. I have looked online for similar projects, as several balloon teams around the world have used RM-60s to measure radiation. Following their pin layout and program, I have been unsuccessful.
//this is taking just the necessary lines to run the geigercounter.
//using geigerPin 8, it works. But why not when I change this to 2, where my
//wire actually is?
int count;
int geigerPin = 8;
int testVar = 0;
void setup() {
Serial.begin(9600);
pinMode(geigerPin, OUTPUT);
attachInterrupt(0, test, RISING);
}
void loop() {
// put your main code here, to run repeatedly:
Serial.println(count * 6);
count = 0;
delay(10000);
}
void test() {
count++;
}
The data reads back to the Serial monitor every 10 seconds. The returned result should be the counts over ten seconds, multiplied by 6 to give us a counts-per-minute reading.
Please refer to the manual befor using any functions. The manual clearly states that your approach is not going to work.
From the Arduino Reference Manual:
attachInterrupt(digitalPinToInterrupt(pin), ISR, mode) (recommended)
attachInterrupt(interrupt, ISR, mode) (not recommended)
attachInterrupt(pin, ISR, mode) (Not recommended. Additionally, this
syntax only works on Arduino SAMD Boards, Uno WiFi Rev2, Due, and
101.)
Example Code
const byte ledPin = 13;
const byte interruptPin = 2;
volatile byte state = LOW;
void setup() {
pinMode(ledPin, OUTPUT);
pinMode(interruptPin, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(interruptPin), blink, CHANGE);
}
void loop() {
digitalWrite(ledPin, state);
}
void blink() {
state = !state;
}

Timer1 acting weird on Arduino UNO (ATMEGA328)

I am trying to implement a simple timer1 example that I saw on YouTube: http://youtu.be/Tj6xGtwOlB4?t=22m7s . The example was in c++ for stand alone ATMEGA328 chip and I am trying to get it to work on the Arduino UNO. Here is my working code:
void setup() {
//initialize port for LED
DDRB = 0b11111111; //initialize port B as output (really only care about 5th bit)
PORTB = 0b00000000; //set ouput values to zero
TCCR1A = 0; //clear control register A (not sure that I need this)
TCCR1B |= 1<<CS10; //no prescaler, turns on CS10 bit of TCCR1B
}
void loop() {
if (TCNT1 >= 255){
TCNT1 = 0; //resets timer to zero
PORTB ^=1<<PINB5; //1<<PINB5 is same as 0b00100000, so this toggles bit five of port b which is pin 13 (red led) on Arduino
}
}
Everything is working, but TCNT1 will only count up to 255. If I set the value in the if-statement to anything higher, the code in the if statement is never executed. Timer1 should be a 16-bit timer, so it does not make sense why the count stops at 255. Is arduino doing something behind the scenes to mess this up? It seems to work just fine in the example on youtube (without arduino).
First of all.... Why do you set the registers? Arduino's only benefit is that it wraps up some functions, so why not use it? Instead of
DDRB = 0b11111111;
PORTB = 0b00000000;
...
PORTB ^=1<<PINB5;
use simply
int myoutpin = XXXX; // Put here the number of the ARDUINO pin you want to use as output
...
pinMode(myoutpin, OUTPUT);
...
digitalWrite(myoutpin, !digitalRead(myoutpin));
I think that probably there are some similar functions for the timer too..
As for your question, I tried this code:
// the setup routine runs once when you press reset:
void setup() {
TCCR1A = 0; //clear control register A (not sure that I need this)
TCCR1B |= 1<<CS10; //no prescaler, turns on CS10 bit of TCCR1B
Serial.begin(9600);
}
// the loop routine runs over and over again forever:
void loop() {
if (TCNT1 >= 12000){
TCNT1 = 0; //resets timer to zero
Serial.println("Timer hit");
}
}
in a simulator and it works well; I should try it with a real Arduino, but I haven't any at the moment... As soon as i get one I'll try to use it
I've encountered the same problem, in the Atmel documentation, I found that other pins influence the counter mode. That is, the pins: WGM13,WGM12,WGM11,WGM10 are 0,1,0,0 respectively, the counter will be in CTC mode, meaning that it will count up to the value of OCR1A instead of (2^16-1) which might be the case in your code.
WGM11,WGM10 are bits 1,0 in TCCR1A and WGM13,WGM12 are bits 4,3 in TCCR1B so setting them to zero should do the job.
I had some thing like this with one of my code. I could not able to find the exact reason for the issue. At last I remove both setup and loop function an replace those with c code.
Then it work fine. If you need those function then start code by clear both TCCR1A and TCCR1B register. I hope this is happened due to Arduino IDE not sure. But it works.

Calling constant complexity O(1) 5 line function significantly impacts performance, why?

I have the following code:
#include <ros.h>
#include <ros/time.h>
#include <sensor_msgs/Range.h>
#define sensNumber 3
#define firstTrigPin 3
#define firstEchoPin 9
ros::NodeHandle nh;
sensor_msgs::Range range_msg;
ros::Publisher pub_range( "/ultrasound", &range_msg);
char frameid[] = "/ultrasound";
int trigPin=firstTrigPin;
int echoPin=firstEchoPin;
void increasepins(){
trigPin++;
echoPin++;
if(trigPin>firstTrigPin+sensNumber){
trigPin=firstTrigPin;
}
if(echoPin>firstEchoPin+sensNumber){
echoPin=firstEchoPin;
}
}
void setup(){
nh.initNode();
nh.advertise(pub_range);
for(int i=0;i<sensNumber;i++){
pinMode(trigPin, OUTPUT);
pinMode(echoPin, INPUT);
increasepins();
}
range_msg.radiation_type = sensor_msgs::Range::ULTRASOUND;
range_msg.header.frame_id = frameid;
range_msg.field_of_view = 0.1;
range_msg.min_range = 0.0;
range_msg.max_range = 6.47;
}
long range_time;
void loop()
{
increasepins();
//publish the adc value every 50 milliseconds
//since it takes that long for the sensor to stablize
long duration, distance;
digitalWrite(trigPin, LOW); // should be high?
delayMicroseconds(2); // make this 20
digitalWrite(trigPin, HIGH);
delayMicroseconds(10); // reset value?
digitalWrite(trigPin, LOW);
distance = pulseIn(echoPin, HIGH)/ 58,2; //sensor constant is 3.4
range_msg.range = distance;
range_msg.header.stamp = nh.now();
range_time = millis() + 50;
range_msg.field_of_view = trigPin;
range_msg.min_range = echoPin;
pub_range.publish(&range_msg);
nh.spinOnce();
}
This above code runs at my funduino at aprox 2 times a second for the whole loop. If I however remove the increasePins method it runs at around 100 times per second. Why the big chance? It seems like such a trivial piece of code (just increase 2 variables and then compare them) make such a large difference?
For reference we are talking about this function:
void increasepins(){
trigPin++;
echoPin++;
if(trigPin>firstTrigPin+sensNumber){
trigPin=firstTrigPin;
}
if(echoPin>firstEchoPin+sensNumber){
echoPin=firstEchoPin;
}
}
Which to me looks really simple when compared to having to wait for the echo to come back and getting the time and that sort of things required for the rest of the code.
I believe that one of the problem is that if you comment the increasePins function in the loop, the compiler will decide to take some of the logic outside the loop. For that you need to look # the assembly code and see what's being done inside the loop.
Another problem may be related to the pin behavior. For example, I read that "pulseIn" function sends a pulse to the pin and measures it's duration. You use it to pulse the "echoPin". I suspect that if you don't change the echoPin you will have a rather constant duration. However, changing the echoPin may lead to a situation where another pin's pulse duration would be longer. To test this, you can try and use pulsePin(echoPin, HIGH, timeout) with a timeout parameter for the case when you did not comment the increasePins function. I expect the loop to run faster.
So i think that the increasePins is not a bottleneck. I recommend making it inline however (could add some speedup if the compiler hasn't done this already).

While loop behaving unexpectedly

I am not sure if this problem is compiler specific or not, but I'll ask anyways. I'm using CCS (Code Composer Studio), which is an IDE from texas instruments to program the MSP430 microcontroller.
As usual, I'm making the beginner program of making the LED blink, located in the last bit of the P1OUT register. Here's the code that DOESN'T work (I've omitted some of the other declarations, which are irrelevant):
while(1){
int i;
P1OUT ^= 0x01;
i = 10000;
while(i != 0){
i--;
}
}
Now, here's the loop that DOES work:
while(1){
int i;
P1OUT ^= 0x01;
i = 0;
while(i < 10000){
i++;
}
}
The two statements should be equivalent, but in the first instance, the LED stays on and doesn't blink, while in the second, it works as planned.
I'm thinking it has to do with some optimization done by the compiler, but I have no idea as to what specifically may be wrong.
The code is probably being optimised away as dead-code. You don't want to spin like that anyway, it's terribly wasteful on CPU cycles. You want to simply call usleep, something like:
#include <unistd.h>
int microseconds = // number of 1000ths of milliseconds to wait
while(1){
P1OUT ^= 0x01;
usleep(microseconds);
}
CCS can optimize code in a way you could never expect (also check the optimization levels in the project properties). Easiest way is to declare the variable with volatile keyword and you are done.

Arduino code anomalies - LCD fails with multiple 'if' statements

I have a question about some code and weird anomalies. This code is placed on a Digispark. The Digispark has a code size limit of 6,010 bytes. When using nested if statements, text is not outputted to the LCD screen (see links below). By commenting out each set seperately I am able to get it working again.
Basic LCD functions:
LCD outputs internal beer temp and ambient air temp. http://imgur.com/S0rYvaa
LCD clears
LCD outputs target temp and heater (relay) status http://imgur.com/OtFXG1K
Variables are of float type.
float inside_temp;
float outside_temp;
float target = 74.00;
//inside_temp and outside_temp are values from 2 ds18b20's
inside_temp = 70.70;
outside_temp = 70.81;
The LCD works when using this code with it commented out like this. The compiled size is 5,928 bytes.
if(inside_temp < target){
//Create a limit so heater isn't crazy hot as 5 gallons takes a while to change temperature.
// float limit = target + 1;
// if(outside_temp > limit){
// digitalWrite(RELAY_PIN, LOW);
// lcd.print("OFF");
// }
// else{
digitalWrite(RELAY_PIN, HIGH);
lcd.print("ON");
// }
}
else{
digitalWrite(RELAY_PIN, LOW);
lcd.print("OFF");
}
The LCD also works with this code. The compiled size is 5,590 bytes.
// if(inside_temp < target){
//Create a limit so the heater isn't crazy hot as 5 gallons takes a while to change temperature.
float limit = target + 1;
if(outside_temp > limit){
digitalWrite(RELAY_PIN, LOW);
lcd.print("OFF");
}
else{
digitalWrite(RELAY_PIN, HIGH);
lcd.print("ON");
}
// }
// else{
// digitalWrite(RELAY_PIN, LOW);
// lcd.print("OFF");
// }
LCD does NOT work when uncommented. The compiled size is 5,992 bytes. All it does is sit there with the backlit on and no text. http://imgur.com/xPAzY0N,DdGdYoI
if(inside_temp < target){
//create a limit so heater isn't crazy hot as 5 gallons takes a while to change temperature.
float limit = target + 1;
if(outside_temp > limit){
digitalWrite(RELAY_PIN, LOW);
lcd.print("OFF");
}
else{
digitalWrite(RELAY_PIN, HIGH);
lcd.print("ON");
}
}
else{
digitalWrite(RELAY_PIN, LOW);
lcd.print("OFF");
}
I don't understand why this happens. Is this occuring because I'm getting too close to maximum size limit? Can I not structure code like this?
I think it's pretty hard to answer this in a conclusive manner, without being able to test it locally.
It does sound very suspicious though, that it breaks when the code size approaches the maximum. On the other hand, it seems to indicate a bug in the tools that it doesn't break "hard", if some limit was exceeded.
Some tips on how to reduce the code size:
Don't use float since the CPU must emulate it. A fixed-point format should be fine for temperatures.
Factor out the function calls to digitalWrite() and lcd.print(), since function calls generate quite a lot of code.
One way of factoring out those calls is doing something like this:
uint8_t relay_pin = LOW;
const char *lcd_text = "OFF";
if(inside_temp < target) {
float limit = target + 1;
if(outside_temp > limit) {
}
else {
relay_pin = HIGH;
lcd_text = "ON";
}
}
digitalWrite(RELAY_PIN, relay_pin);
lcd.print(lcd_text);
This uses the fact that we want to always update the LCD and relay, so we can always call the functions. Then we use variables to hold the desired values, since assignments are usually cheaper (in terms of code size, here) than function calls.
Don't forget the small size of RAM, as the Attiny85 only has 512 bytes of SRAM, compared to the 328's 2K. You may just be running out of RAM. I learned that when it runs out, it just kind of sits there.
I suggest reading the readme from this library to get the FreeRAM. It mentions how the ".print" can consume both RAM and ROM.
I always now use
Serial.print(F("HELLO"));
versus
Serial.print("HELLO");
as it saves RAM, and this should be true for lcd.print. Where I always put a
Serial.println(freeMemory(), DEC); // print how much RAM is available.
in the beginning of the code, and pay attention. Noting that there needs to be room to run the actual code and recurse into it.
unwind's example of factoring the .print out to only one instance, is using a variable. Which actually does similar as the F() (older getPSTR()) in that it is no longer a const string. So it actually use less RAM too.