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.
Related
Before I get started I want to make it clear I am still a beginner in both C++ and Arduino electronics, so the answer may be painfully obvious to someone with even a week more experience than me.
But to me, it is a total mystery. Thank you for any help.
To begin this is the code:
int potPosition;
int delayTime;
void setup() {
Serial.begin(9600);
pinMode(13, OUTPUT);
pinMode(12, OUTPUT);
pinMode(11, OUTPUT);
pinMode(10, OUTPUT);
pinMode(9, OUTPUT);
}
void loop() {
potPosition = analogRead(A0);
Serial.println(potPosition);
if (potPosition < 16) {
delayTime == 16;
} else if (potPosition >= 16){
delayTime == analogRead(A0);
}
digitalWrite(13, HIGH);
digitalWrite(12, HIGH);
digitalWrite(11, HIGH);
digitalWrite(10, HIGH);
digitalWrite(9, HIGH);
delay(delayTime);
digitalWrite(13, LOW);
digitalWrite(12, LOW);
digitalWrite(11, LOW);
digitalWrite(10, LOW);
digitalWrite(9, LOW);
delay(delayTime);
}
This is all written in the Arduino Genius IDE and I am outputting it to a Sparkfun electronics Arduino with 5 LEDs connected to it controlled by a potentiometer, which sets the on and then off delay by outputting a value I take as milliseconds of delay to make the LEDs flash. All the electronic wiring is correct, as when I remove the if statement and just run it as normal, everything works as I'd expect.
So you may be wondering, what's the issue? Well, currently when the potPosition exceeds a certain value - a delay value in ms - which is too low, the LEDs no longer appear to flash, as the delay is far to small for the flash to be visible. This is an expected outcome, as a delay of nearing 0ms is extremely small.
What I am trying to do with my if statement:
if (potPosition < 16) {
delayTime == 16;
} else if (potPosition >= 16){
delayTime == analogRead(A0);
}
is if the potPosition - the value of the dial which is the ms delay - goes below a certain number (the last value which is a long enough delay for a flashing effect to still be visible) set my delayTime back to that value and if the potPosition is that value or anything higher, then set the delayTime to whatever delay the potentiometer is outputting.
However, currently, when I download this code onto my Arduino all my lights appear dim (the same way they do when my delay time is extremely low) no matter what value my potentiometer is outputting, which is not what I want it to do, and I have no idea why my if statement is not resetting my delay time correctly.
I've never really coded C++ before starting on a Sparkfun electronics kit, and only ever used Python, so I may be missing a key aspect of programming in C++, but I have no idea what that would be.
Any help with this issue would be great, and I am more than happy to answer any questions.
This delayTime == analogRead(A0); and this delayTime == 16; are comparisons, i.e. delayTime remains uninitialised and you get undefined behaviour.
I bet you want to write a value there, as in delayTime = analogRead(A0); or maybe delayTime = potPosition; (same for the other ==). The latter seems more plausible to me, so as to only have one analog read per loop iteration.
Im trying to generate a knock knock detector using Arduino and a Piezo Buzzer. The one used in this proyect is the same as in this picture
Using this code
const int outputPin = 8; // led indicator connected to digital pin
const int knockSensor = A0; // the piezo is connected to an analog pin
const int thresholdHIGH =150; // threshold value to decide when the detected knock is hard (HIGH)
const int thresholdLOW = 120; // threshold value to decide when the detected knock is gentle (LOW)
const int secretKnockLength = 4; //How many knocks are in your secret knock
/* This is the secret knock sequence
* 0 represents a LOW or quiet knock
* 1 represents a HIGH or loud knock
* The sequence can be as long as you like, but longer codes increase the difficulty of matching */
const int secretKnock[secretKnockLength] = {0, 0, 1, 0};
int secretCounter = 0; //this tracks the correct knocks and allows you to move through the sequence
int sensorReading = 0; // variable to store the value read from the sensor pin
void setup() {
//Set the output pin as an OUTPUT
pinMode(outputPin, OUTPUT);
//analogWrite(knockSensor, LOW);
//Begin Serial Communication.
Serial.begin(9600);
}
void loop() {
// read the piezo sensor and store the value in the variable sensorReading:
sensorReading = analogRead(knockSensor);
Serial.print ("Valor del Sensor: ");
Serial.println(sensorReading);
// First determine is knock if Hard (HIGH) or Gentle (LOW)
//Hard knock (HIGH) is detected
if (sensorReading >= thresholdHIGH) {
//Check to see if a Hard Knock matches the Secret Knock in the correct sequence.
if (secretKnock[secretCounter] == 1) {
//The Knock was correct, iterate the counter.
secretCounter++;
Serial.println("Correct");
} else {
//The Knock was incorrect, reset the counter
secretCounter = 0;
Serial.println("Fail");
digitalWrite(outputPin, LOW);
}//close if
//Allow some time to pass before sampling again to ensure a clear signal.
delay(100);
//Gentle knock (LOW) is detected
} else if (sensorReading >= thresholdLOW) {
//Check to see if a Gentle Knock matches the Secret Knock in the correct sequence.
if (secretKnock[secretCounter] == 0) {
//The Knock was correct, iterate the counter.
secretCounter++;
Serial.println("Correct");
} else {
//The Knock was incorrect, reset the counter.
secretCounter = 0;
Serial.println("Fail");
}//close if
//Allow some time to pass before sampling again to ensure a clear signal.
delay(100);
}//close if else
//Check for successful entry of the code, by seeing if the entire array has been walked through.
if (secretCounter == (secretKnockLength) ) {
Serial.println("Welcome");
//if the sececret knock is correct, illuminate the LED for a couple seconds
digitalWrite(outputPin, HIGH);
//Reset the secret counter to 0.
secretCounter = 0;
}//close success check
}//close loop".
My problem is the buzzer is detecting nothing. I dont know if this is because the buzzer is not the correct one or something else.
Any idea?
If the problem is that the buzzer isn't making any noise, then you should be using the tone() function in your code. (https://www.arduino.cc/reference/en/language/functions/advanced-io/tone/)
Your code compiles successfully, so if that's not your issue, then there's probably something wrong with your project's wiring.
To detect knocking, you need to use piezo without driver circuit.
according to Arduino tutorial said that avoid piezo with plastic housing.
You should use piezo that look like metal disc like this
(image from sparkfun)
Note : piezo with plastic housing might contain drive circuit or not piezo at all (it might be magnetic buzzer)
unsigned long t;
boolean isHigh;
#define BUZZER_PIN 3
void setup() {
// put your setup code here, to run once:
pinMode(BUZZER_PIN, OUTPUT);
isHigh = false;
t = micros();
}
void loop() {
playNote('c');
}
void playNote(char note) {
unsigned long timeToWait;
unsigned long timeToPlayTheNote = millis();
while (timeToPlayTheNote - millis() < 1000) {
if (note == 'c') {
timeToWait = 1911;
}
if (micros() - t > timeToWait) {
if (!isHigh) {
digitalWrite(BUZZER_PIN, HIGH);
isHigh = true;
} else {
digitalWrite(BUZZER_PIN, LOW);
isHigh = false;
}
t = micros();
}
}
}
I don't know why this won't work. I used to play a frequency every 1,000 microseconds but is there any way to make this simpler as well? Also, with this method I have to do (1/f)/2 and then convert that value from seconds to microseconds and use that as the value for timeToWait.
Initialization of ˋtimeToWait` should obviously be outside of the loop.
An array could be used for timing data.
ˋt` should probably be initialized inside ˋplayNoteˋ
Alternatively, you might use an enum for delay associated to a note.
enum class notes
{
C = 1911
};
Well, all suggestion assume that you don't want to compensate for drifting offsets.
Buzzers have a fixed frequency. They don't work like speakers at all. You will get better results with a real speaker. Don't forget to put a capacitor in series with it so the speaker sees an AC signal, you can fry a speaker quite easily if you feed it a DC signal..
For best results, you should use 2 x 47uF to 100uF electrolytic capacitors back to back, with the negative poles joined together, one positive to the 'duino and the other positive pole connected to the speaker. With higher capacitance, you'll get more bass.
Why don't you use a PWM at 50% (128) and change the PWM frequency to generate the sound? You could use the Timer1 or Timer3 library for that. Letting the hardware do the work would be more presise and would free your application for other tasks, such as reading a keyboard.
https://playground.arduino.cc/Code/Timer1
Setting the PWM at 0% with an analogWrite() would cut the sound.
Really simple question but I'm not entirely sure how to incorporate a for loop in the if statement I have. Context: I have a humidifier I am trying to automate based on the humidity of the room. I'm using an ardiuno, dht11 humidity sensor and a servo. The humidifier knob has three settings (high low off) and so the servo has three positions. I have the code running so the servo turns appropriately according to the humidity level. The issue is that it fluctuates very easily. To correct that I'm looking to incorporate a for loop so that after let say 60 one second iterations of the humidity being greater than 55 the servo moves. I tried to add a for loop but it doesn't seem to be working.
But this is only my solution based on the little programming I know. If there is a better solution or even an equally viable alternative I'd love to know. I'm currently studying mechanical engineering but I'm finding that to really make something one needs a background in electronics and code. I'm trying to learn both independently through a series of projects and so I'm quite eager to learn. Hopefully this helps explain why I'm asking such a simple questions to begin with.
#include <dht.h>
#include <Servo.h>
Servo myservo;//create servo object to control a servo
dht DHT;
#define DHT11_PIN 7 // pin for humidity sensor ( also measure temp)
void setup() {
myservo.attach(9);//attachs the servo on pin 9 to servo object
myservo.write(0);//statting off position at 0 degrees
delay(1000);//wait for a second
Serial.begin(9600);
}
void loop() {
int chk = DHT.read11(DHT11_PIN); // the follow is just so that I can see the readings come out properly
Serial.print("Temperature = ");
Serial.println(DHT.temperature);
Serial.print("Humidity = ");
Serial.println(DHT.humidity);
delay(500);
if (DHT.humidity > 55) // here is where my code really begins
{
for (int i=0; i>60; i++); // my goal is to execute the follow code after the statement above has been true for 60 one second iterations
{
myservo.write(0);//goes to off position
delay(1000);//wait for a second
}
} else if (DHT.humidity > 40 ) {
for (int i=0; i>60; i++); // same thing here
myservo.write(90);//goes to low position
delay(1000);//wait for a second
}
else
{
for (int i=0; i>60; i++);
myservo.write(180);//goes to high position
delay(1000);
}
} // end of void loop()
Just addressing your question, the following line is incorrect:
for (int i=0; i>60; i++);
Two things:
1) The second statement in the for loop describes the conditions on which it executes. The way it is written, it will only execute when i>60 (not what you want according to the comments).
2) The semicolon after the for statement makes the next block unassociated.
Correct that line to the following:
for (int i=0; i<60; i++)
See the following for more information:
https://www.tutorialspoint.com/cprogramming/c_for_loop.htm
It would probably be helpful to examine your compiler warnings, and/or set a higher warning level, to catch these type of things early (this is, of course, somewhat compiler dependent).
I guess you trying kind of de-bouncing at you need humid level stay in same range for some period.
First, I define conversion function to map humid level to state
#define HUMID_OFF 1
#define HUMID_LOW 2
#define HUMID_HIGH 3
byte state_conv (float humid_level){
if (humid_level > 55) return HUMID_OFF ;
else if (humid_level > 40 ) return HUMID_LOW ;
else return HUMID_HIGH ;
}
Second I will check changing of state and use millis() to count time while current state is steady. if counting time are longer than threshold then change the actual state.
/*Global variable*/
byte actual_state;
byte flag_state;
void setup (){
// Do things that necessary
float humid = dht.readHumidity();
/*Initialize value*/
actual_state = state_conv(humid);
flag_state= state_conv(humid);
}
void loop(){
static unsigned long timer = millis();
float humid = dht.readHumidity();
byte crr_state = state_conv(humid);
if (crr_state != actual_state ){// if state is changing
if (flag_state != crr_state){
/if crr_state change form last iteration then reset timer
flag_state = crr_state;/
timer = millis();
}
else if (millis() - timer > 10000){
//if crr_state not change for 10000 ms (10 second)
actual_state = crr_state; // update actual state to crr_state
}
}
// After this use actual_state to control servo
if (actual_state == HUMID_OFF ){
myservo.write(0);//goes to off position
}
else if (actual_state == HUMID_LOW ){
myservo.write(90);//goes to low position
}
else if (actual_state == HUMID_HIGH ){
myservo.write(180);//goes to high position
}
}
DHT.humidity returns a float, so if you want to compare, then first store this in an int, and then compare.
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).