Go Down

Topic: Too many "if" statements? (Read 10892 times) previous topic - next topic

Jim_Socks

John:

Very cool.  I'm beginning to grasp this function-making business now.

I tried your example, and it gave me a "flashdrink not declared in this scope" message when I put the

flashdrink(vodka, 1);

line inside my void loop.  I just don't have time to figure out why right this second, as I am heading out to a party.  If I can get it to work though that will be really cool, so I'll take a better look at it tomorrow.  Thanks for helping me learn some new tricks!  (as a side note, what is the "& amt" doing?  I have never seen it before)

PaulS:

Thanks for clarifying.  I thought the F() macro came from the library until you said otherwise.  All a bit over my head still to be honest.  I only tried it because someone mentioned moving a bunch of my stuff to flash memory would save me a bunch of RAM. 

GoForSmoke


Quote
Use the F macro to wrap your constant strings


Awesome- I will!

Quote
Move the for loops into the flash() function.


I would, but each "for" loop is different.  The number of times to flash changes every time (this corresponds to which bottle is running low- so if bottle number 11 is getting low, then that "for" loop knows to flash the light eleven times) 


if (rumlt >= 180) {
for (int x = 0; x < 11; x++){


and here is the one for bottle #2:

if (rumlt >= 180) {
for (int x = 0; x < 2; x++){



That is fine but notice that only the details change. One function should do for all your pumps. You feed in the details and it gives the answer(s). But I see that you need to learn some programming basics before the path becomes clear to you.

Arrays -- you have loads of byte variables that belong together but have different names, then you write a function or more for each just to get the names covered.

Example, this _________
Code: [Select]
//begin drink integers

byte vodka1st = 0x1;
byte vodka2nd = 0x0;
byte vodka3rd = 0x0;
byte rumlt1st = 0x2;
byte rumlt2nd = 0x0;
byte rumlt3rd = 0x0;
byte rumdk1st = 0x4;
byte rumdk2nd = 0x0;
byte rumdk3rd = 0x0;
byte whiskey1st = 0x8;
byte whiskey2nd = 0x0;
byte whiskey3rd = 0x0;
byte gin1st = 0x10;
byte gin2nd = 0x0;
byte gin3rd = 0x0;
byte brandy1st = 0x0;
byte brandy2nd = 0x2;
byte brandy3rd = 0x2;
byte tequila1st = 0x4;
byte tequila2nd = 0x0;
byte tequila3rd = 0x0;
byte amarretto1st = 0x20;
byte amarretto2nd = 0x0;
byte amarretto3rd = 0x0;



Can become this;
Code: [Select]
//begin drink integers

enum { VODKA, RUMLT, RUMDK, WHISKEY, GIN, BRANDY, TEQUILA, AMARRETTO };

byte drinkMix[ 8 ][ 3 ] = { 1,0,0, 2,0,0, 4,0,0, 8,0,0, 16,0,0, 0,2,2, 4,0,0, 32,0,0  };



Only for more than 8 ingredients. You access the parts you want through 2 indexes.
The enum gives you code names for the 1st index, I don't know what the 2nd index is about.

So instead of gin3rd you have drinkMix[ GIN ][ 2 ], 2 as in 0-1-2 not 1st-2nd-3rd. The same array provides for easy-to-switch-between data for all your ingredients.

A function would take maybe the 1st index and use the 2nd index in itself to pass back whatever you need. Or maybe it takes more than 1st index, that's up to you.

The idea is to reduce your code by generalizing wherever you can. If I had 100 things that each had to be stirred a certain number of times then I would write 1 stir function and 1 array to cover the lot.

Note that arrays of constant values or text (data that does not change during run time) can be put into flash easily. Keep your limited RAM for what your code is working on -now-. ;^)

One definition of a computer program is: Program = Code + Data.

When/if you do serious debugging, you will see how true that can be. Often a fault in data can have a programmer tearing out hair while fixated on code alone. That's a good time to take a break and come back when a fresh look becomes possible.



1) http://gammon.com.au/blink  <-- tasking Arduino 1-2-3
2) http://gammon.com.au/serial <-- techniques howto
3) http://gammon.com.au/interrupts
Your sketch can sense ongoing process events in time.
Your sketch can make events to control it over time.

keeper63

GoForSmoke covers what I was going to say; basically, you need a function that will take those arrays (call the function "pump()" or "pour()" - and put the arguments in the parentheses), and generalize the loops you use to "pour/pump" the drink; one of those arguments will be the pump pin of course (actually, it will be an index into the array for the pump pins or something like that). You'll be able to reduce a lot of stuff.

But there is something else I noticed about the code.

You are dumping a lot of strings out via serial.print(); you comment these as saying they are going to an iPad (or iPhone); something along those lines? Why?

Why not just dump a byte or two (or a few more) out - and have the app on the other side (on the device) decode that data into the messages you want to have displayed. Most of the data you dump is fairly repetitive; the little that isn't could be replaced by an index value or something that tells the device application "Print out the message: Shake and stir, top with an olive - Delish!" - instead of storing (in RAM or Flash) and outputting it via the serial port. Your application and device has infinitely more room to work with than the Arduino does.
I will not respond to Arduino help PM's from random forum users; if you have such a question, start a new topic thread.

johncc


John:

Very cool.  I'm beginning to grasp this function-making business now.

I tried your example, and it gave me a "flashdrink not declared in this scope" message when I put the

flashdrink(vodka, 1);



Hi Jim---

It should work, could you attach your code to a message and we can take a look?

John

johncc


...
Code: [Select]
//begin drink integers

enum { VODKA, RUMLT, RUMDK, WHISKEY, GIN, BRANDY, TEQUILA, AMARRETTO };
byte drinkMix[ 8 ][ 3 ] = { 1,0,0, 2,0,0, 4,0,0, 8,0,0, 16,0,0, 0,2,2, 4,0,0, 32,0,0  };



Only for more than 8 ingredients. You access the parts you want through 2 indexes.
The enum gives you code names for the 1st index, I don't know what the 2nd index is about.

So instead of gin3rd you have drinkMix[ GIN ][ 2 ], 2 as in 0-1-2 not 1st-2nd-3rd. The same array provides for easy-to-switch-between data for all your ingredients.
...


True enough, but I would rather see something like
Code: [Select]

struct DrinkPump {
        int id;
const char* name;
int first, second, third;     // rename these better
unsigned int totalpoured;  // qtr-oz
int minamt,maxamt;
    ...
        void checkEmpty();
};

DrinkPump pumps={
  {1,"Vodka",  1,0,0, 180, 190},
  {2,"Tonic",   1,3,0, 180, 190},
  {3,"Tequila", 1,0,7, 360, 380}  // we use these by the "handles" in texas
...
};

// Aka (I think) "flash count" in previous code
void Pump::checkEmpty( Pump &pump)
{
if (totalpoured > maxamt) {
cout << "Resetting " << name << endl;
totalpoured = 0;
}
if (totalpoured >= minamt) {
cout << "flashing led for " << name << ", " << flashcount << " times" << endl;
flashEmptyLED(id);  // flash the number of times per the id
}
}


So you end up with
     for (int i=0; i< numberofpumps ; i++)
          pumps.checkEmpty();

My 2 pesos :)

John

JackSac67

#35
Jan 01, 2013, 11:37 pm Last Edit: Jan 01, 2013, 11:39 pm by JackSac67 Reason: 1
There's always very simple ways to reduce memory usage in your code bit by bit (no pun intended) as well. For example for declaring variables that never change like pin modes with variables assigned to numbers, you might want to define those variables as constant variables that never change with "const". Since the variable is never going to change (like a pinmode), there is no reason for the Arduino to keep track of it all the time.

Jim_Socks

#36
Jan 02, 2013, 02:00 am Last Edit: Jan 02, 2013, 02:20 am by Jim_Socks Reason: 1
Jacksac67:

Awesome idea for a quick grab at some memory.  I changed a whole bunch of my integers and bytes to const and that freed up about 100 in memory.  THAT, when combined with using the flash library to move all of my serial.print messages into flash memory, brought me from 107 memory free, to 771! (I have a small library running constantly printing my available memory- strictly for debugging purposes while I build the program- it won't be in the final version).  I would LIKE to see if I can alter my Udp.write header file like I modified my print.h file, to accept the F macro as well.  As a gentleman mentioned earlier though- I very well might be barking up the wrong tree with the entire idea of using the flash library to begin with...  hmmm

Judging by other's suggestions though- changing all these bytes to const is a temporary measure as it seems soon I will be building some fancy arrays to handle many of these integers?  Thanks for the knowledge about it- I'll be keeping that trick in my hip pocket!

Cr0sh:

The reason the program is printing out to the iPad (i know it SAYS iphone, but it's not, I am running the program from my iPad) instead of sending a small code for the iPad app to interpret into the intended line of text is that I am using an arduino interface app on the iPad that I didn't design, and it doesn't come with any feature like that.  All it has is a bunch of configurable buttons strictly for sending info out, and one monitor area for printing messages that come in, exactly as they arrive.  The app is VERY handy and awesome, but translating incoming messages into something else do display just isn't something it was designed to do.

Johncc:

Code attached!  You'll find the functions "definition" at the VERY bottom, and you'll find where I use the function under the WARNING LIGHTS header, under vodka.

GoForSmoke and Johncc:

Wow.  This stuff looks ideal (still a wee over my head, but once I get my hands dirty in it I'm sure it will become more clear)  You have made it obvious that I need to completely re-design all my crazy variables.  I am going to start by directly copying some of your examples, googling an array tutorial for some down-n-dirty know-how, and seeing if I can make some sense of it all.

Just for fun, here is a quick snapshot of the iPad control screen.  It is a work in progress still, but the general idea is there:


GoForSmoke

Arrays of structs or classes are better than multiple arrays of bytes for organization but I didn't want to hit you with that when you hadn't absorbed arrays yet. If you can get around using classes though, they allow you to bundle data together with functions under the same name. It can help your code make easier to read sense, easier debug too. Operative word is *can*.

Whatever you do, make some practice code to test ideas with. Bend it and break it just to see what not to do as well as the first thing that works and you will improve for the knowledge gained.


1) http://gammon.com.au/blink  <-- tasking Arduino 1-2-3
2) http://gammon.com.au/serial <-- techniques howto
3) http://gammon.com.au/interrupts
Your sketch can sense ongoing process events in time.
Your sketch can make events to control it over time.

johncc

Looks good Jim!  Where did you get that iOS UI app?

I corrected the functions, paste these instead:
Code: [Select]

//////////////////////////////// Warning Light Blink ///////////////////////////

void flashdrink(byte &amt, byte flashcount , int min, int max){
  if ( amt > max) amt = 0;
  if ( amt >= min) {
    flash( flashcount);
    delay( warn);
  }
}

void flashdrink(byte &amt, byte flashcount)
{  flashdrink( amt, flashcount, 180, 190); }

void flash(byte flashcount){
  for (byte x = 0; x <= flashcount; x++)  {
      digitalWrite(LED, 1);
      delay (250);
      digitalWrite(LED, 0);
      delay (250);
  }
}


Then your // W A R N I N G  L I G H T S /// can look like this:
Code: [Select]
flashdrink(vodka, 1);
flashdrink(rumdk, 2);
flashdrink(whiskey, 3);
flashdrink(gin  , 4);
flashdrink(brandy, 5);
...
flashdrink(water, 22);


Cheers!
John

P.S. you asked about the &amt. Without it, a copy of the value (e.g. vodka) gets sent to the parameter "amt" in the function.  With the &, a reference to the "location" of where the value is stored, is sent to the amt parameter.  The difference is that reference allows the function code to change the value of "vodka" (reset it to zero).

Jim_Socks

#39
Jan 02, 2013, 06:56 am Last Edit: Jan 02, 2013, 06:58 am by Jim_Socks Reason: 1
GoForSmoke:

That sounds like a plan.  I'll jump into learning me some of this array magic as soon as I can.

Johncc:

The code works brilliantly!  It doesn't seem to free up memory space- but it doesn't take up any extra either, AND it sure makes my warning blink section MUCH more manageable.  It's fantastic!  And thanks for clueing me in about the &amt...  knowing that helps me understand how it all works.

As for the app, it's called "Ardumote" and it is available on the app store.  The website is broken (links don't work) but I have the URL to the tutorial that explains how to set it up, and I also have an example sketch file if you are interested.  Despite the website for the app being complete garbage- the app is actually fantastic and the developer is VERY responsive to email.  I have emailed him twice with questions/issues and both times he emailed me back with answers/sketch files within the hour!

Question on a different topic:

Do you know of a way to make

Code: [Select]
Udp.write(F("Amaretto Mist- Serve with a lime wedge.  Enjoy!"));

work just like

Code: [Select]
Serial.print(F("Amaretto Mist- Serve with a lime wedge.  Enjoy!"));

?

I looked in the core files for a "write.h" file... but when I didn't find one I took a look in the EthernetUdp library folder... not there either.  In fact there aren't ANY .h or .cpp files in there...  weird.

ALTERNATIVELY- can the Udp.write send a string instead?  (I can't seem to get it to.)

For instance, something like this:

Quote

      FLASH_STRING(AMON, "Amaretto Mist- Serve with a lime wedge.  Enjoy!");


      Udp.beginPacket(iPhoneIP,iPhonePort);
      Udp.write(AMON);   
      Udp.endPacket();



In this way I can store all of my text snippets in flash memory- when I did a test removing only these text snippets from the sketch- it saved me TONS of RAM.  It sure saved me a bunch when I did it for these:

Quote

void water1()
{
  digitalWrite(latch, 0);
  shiftOut(data, clock,MSBFIRST, water1st);
  shiftOut(data, clock, MSBFIRST, water2nd);
  shiftOut(data, clock, MSBFIRST, water3rd);
  digitalWrite(latch, 1);
  water = water + 4;
  Serial.print(F("Water has now pumped "));
  Serial.print(water);
  Serial.print(F(" quarter ounces, or around "));
  Serial.print(water / 4);
  Serial.println(F(" full ounces.")); 
  delay(o);
}




Or, like a previous poster had alluded- do you think putting these lines of text into flash memory would be a waste of my time to pursue?

GoForSmoke

Not a waste since you don't have tons of ram to begin with.

The strings have to fit in flash in the first place if you don't store them in EEPROM or externally like on an SD card or external chip or as suggested already, in that iPad.

Only thing is that flash reads slower than ram but less than a microsecond slower.

When you're looking into arrays, read up on pointers (memory address holders) at the same time. Walking a pointer through an array has a grace you won't get using indexes. Arrays and pointers kind of go together.
You can even make arrays of function pointers and choose which function to call through data rather than a mess of code in the right situations.

1) http://gammon.com.au/blink  <-- tasking Arduino 1-2-3
2) http://gammon.com.au/serial <-- techniques howto
3) http://gammon.com.au/interrupts
Your sketch can sense ongoing process events in time.
Your sketch can make events to control it over time.

johncc


Do you know of a way to make

Code: [Select]
Udp.write(F("Amaretto Mist- Serve with a lime wedge.  Enjoy!"));
work just like
Code: [Select]
Serial.print(F("Amaretto Mist- Serve with a lime wedge.  Enjoy!"));
?
...
ALTERNATIVELY- can the Udp.write send a string instead?  (I can't seem to get it to.)


I think you may be able to write a function in your own sketch something like this:
Code: [Select]

   void udpWrite( const __FlashStringHelper *f) {
Udp.beginPacket(iPhoneIP,iPhonePort);
Udp.write((const char *)f);    // Send Message back to iPhone
Udp.endPacket();
   }

and then call it like this
Code: [Select]

   kahlua15(); vodka15(); End();
   udpWrite( F("Klingon koffee- Serve on the rocks.  I know... too late!"));


But I'm not sure of the details on this, I have never messed with that technique before.  I'm sure others here have and can help with troubles with it...

John

dc42

#42
Jan 02, 2013, 07:38 pm Last Edit: Jan 02, 2013, 10:10 pm by dc42 Reason: 1

I think you may be able to write a function in your own sketch something like this:
Code: [Select]

  void udpWrite( const __FlashStringHelper *f) {
Udp.beginPacket(iPhoneIP,iPhonePort);
Udp.write((const char *)f);    // Send Message back to iPhone
Udp.endPacket();
  }

and then call it like this
Code: [Select]

  kahlua15(); vodka15(); End();
  udpWrite( F("Klingon koffee- Serve on the rocks.  I know... too late!"));


But I'm not sure of the details on this, I have never messed with that technique before.  I'm sure others here have and can help with troubles with it...


Try this:

Code: [Select]

  void udpWrite( const __FlashStringHelper *f) {
Udp.beginPacket(iPhoneIP,iPhonePort);
       const prog_char *p = (const prog_char*)f;
       for (;;)
       {
          char c = pgm_read_byte_near(p++);
          if (c == 0) break;
          Udp.write(c);
       }
Udp.endPacket();
  }
Formal verification of safety-critical software, software development, and electronic design and prototyping. See http://www.eschertech.com. Please do not ask for unpaid help via PM, use the forum.

Jim_Socks

#43
Jan 03, 2013, 07:44 am Last Edit: Jan 03, 2013, 07:46 am by Jim_Socks Reason: 1
Johncc and Dc42:

I tried it, and it worked without flaw.  Three days ago I had 100 free RAM, now, with the exact same functionality (and MUCH cleaner code I might add) I have 1,583 free RAM.

I leave tomorrow on a two-week business trip, but when I return I'll be jumping right into arrays & pointers, looking into how to store these strings on the Ethernet shields SD card instead of flash (might come in handy), and possibly making it so that all of my pumps to work under one function.

Possibly with something like this:

Code: [Select]

  void delaytime(int del)    // to be used in the "liquid();" function below... this will determine how long the pump runs ie. how much liquid is poured
{
  if (del = 125);{    // a "dash"
delay(375); }
   if (del = 25);{    // 0.25oz
delay(750); }
   if (del = 33);{    // 0.33oz
delay(1000); }
   if (del = 5);{     // 0.5oz
delay(1500); }
   if (del = 75);{    // 0.75oz
delay(2250); }
   if (del = 1);{     // 1oz
delay(3000); }
   if (del = 15);{    // 1.5oz
delay(4500); }
   if (del = 2);{     // 2oz
delay(6000); }
}

  void vodka()          //one of these for each liquor, to be used in the "any function" input condition below
{
     
  shiftOut(data, clock,MSBFIRST, vodka1st);   //once I learn arrays this may not be neccisary... we'll see
  shiftOut(data, clock, MSBFIRST, vodka2nd);
  shiftOut(data, clock, MSBFIRST, vodka3rd);
 
 
}
   void liquid(any function, byte &amt, byte x, byte oz)  // I don't REALLY know how to make a functions input condition another function yet...
  {
    digitalWrite(latch, 0);
    any function();                   //  for example, vodka(); or cola(); would go here. I am not sure how yet though
    digitalWrite(latch, 1);
  Serial.print(F("(byte &amt) has now pumped "));
  Serial.print(byte &amt);
  Serial.print(F(" quarter ounces, or around "));
  Serial.print(byte &amt / 4);
  Serial.println(F(" full ounces."));   
 
    byte &amt = byte &amt + oz;  //for counting 1/4 ounces

  delaytime(x);
  }
 
 
  // called like this:
 
  liquid(vodka, vodka, 75, 3);  liquid(cola, cola, 5, 2); liquid(orange, orange, 2, 8);


Obviously the code above is just an example of an idea I'm beginning to form and was not intended to be any sort of working code.

It's late, however, and I have a big trip starting tomorrow- so I am going to call it a night.  Thanks again for all of your help- these last few days have taught me a ton.  You guys are the best.  You haven't seen the last of me  ;)

GoForSmoke

If you ditch those delay() calls you could probably run many drink mixers off 1 Arduino.
The beginning of understanding that is in the Blink Without Delay example that came with your IDE.

1) http://gammon.com.au/blink  <-- tasking Arduino 1-2-3
2) http://gammon.com.au/serial <-- techniques howto
3) http://gammon.com.au/interrupts
Your sketch can sense ongoing process events in time.
Your sketch can make events to control it over time.

Go Up