Dip switch to change code if... then.. question

Howdy, all. I hope this is the right place to post this question.

As I move forward with my project and design I came up with the following question. Can I wire a dip switch on my board that will tell my particle device what formula/part of the code to run?

Example. I have multiple size meters. They will each have a different flow rate, and reading scale. Instead of having different code with each size meter I want to have a dip switch that can be selected to tell my particle code which formula to use base on which dip switch is selected.

I would use a 4 to 5 channel dip switch but not sure what pins to write those too as well as if they should be set to high or low… or if this will even work.

Here is my original code.

float flow = map(adc,2139.0, 10614.0, 0.0, 200.0);
String F = String::format("%.1f", flow);

Lets say that would be for a one inch meter with a flow of 0 to 200. I would set this as dip swith 2. Example

I would have other sizes so the smaller unit would be dip switch 1 and the code obviously more complicated than this but her is a general idea.

if…
dip switch set to 1 then pin B2 is High.

then…
float flow = map(adc,1000.0, 8000.0, 0.0, 40.0);
String F = String::format("%.1f", flow);

if….
dip switch set to 2 then Pin B3 is High

then…
float flow = map(adc,2139.0, 10614.0, 0.0, 200.0);
String F = String::format("%.1f", flow);

and so on.

Any help or suggestions is appreciated. If someone already did this please link me to the post. I was not able to find anything yet but I may have missed it.

Thank you,
Tom

1 Like

@GasGen, with each dip switch associated with a pin, you can simply treat the switches as binary value:

dip = 0;
if (digitalRead(B2) dip+=1;
if (digitalRead(B3) dip+=2;
if (digitalRead(B4) dip+=4;
if (digitalRead(B5) dip+=8;

So with two switches you can select 4 configurations. With 3 switches you get 8 configurations and so on. Let’s assume you go with two switches or jumper or whatever acts like a switch.

Now, you simply use the value of “dip” to get key values for equations from a lookup table (an array of structures for example) and use those in a single formula.

float flow = map(adc, meters[dip].fromLow, meters[dip].fromHigh, meters[dip].toLow, meters[dip].toHigh);

The structure would look like this:

struct meter_lookup {
  float fromLow;
  float fromHigh;
  float toLow;
  float toHigh;
};

const meter_lookup meters[4] = {
 { 2139.0, 10614.0, 0.0, 200.0 },
 { 1000.0, 8000.0, 0.0, 40.0 },
 { 1000.0, 8000.0, 0.0, 40.0 },  // whatever values for third meter type
 { 1000.0, 8000.0, 0.0, 40.0 }   // whatever values for fourth meter type
}
3 Likes
#define GPIO0	D0
#define GPIO1	D1
#define GPIO2	D2
#define GPIO3	D3
#define GPIO4	D4

// Shift Left times
#define SWITCH_1    0
#define SWITCH_2    1
#define SWITCH_3    2
#define SWITCH_4    3
#define SWITCH_5    4


/* -----------------------------------------------------------------------------
  readDipSwitch()

 -----------------------------------------------------------------------------*/
uint8_t readDipSwitch(void)
{

	return  (digitalRead(GPIO0) << SWITCH_1) |
		(digitalRead(GPIO1) << SWITCH_2) |
		(digitalRead(GPIO2) << SWITCH_3) |
		(digitalRead(GPIO3) << SWITCH_4) |
		(digitalRead(GPIO2) << SWITCH_5);
}

/* -----------------------------------------------------------------------------
  checkState()

 -----------------------------------------------------------------------------*/
void checkState(void)
{
	float flow;

	switch(readDipSwitch())
	{
		case STATE_1:	flow = map(adc,1000.0, 8000.0, 0.0, 40.0);  break;	
		case STATE_2:	flow = map(adc,2139.0, 10614.0, 0.0, 200.0);  break;	
		case STATE_3:	flow = map(adc,2139.0, 10614.0, 0.0, 200.0);  break;	
		case STATE_4:	flow = map(adc,2139.0, 10614.0, 0.0, 200.0);  break;	
		case default: // Whoops something hankey was detected; break;
	}

	String F = String::format("%.1f", flow);
}

/* -----------------------------------------------------------------------------
  setup()

 -----------------------------------------------------------------------------*/
void setup()
{

	// Set pins as Inputs, when you close the switch it sould then connect the pin to GND.
  pinMode(GPIO0, INPUT_PULLUP); // sets pin as input
  pinMode(GPIO1, INPUT_PULLUP); // sets pin as input  
  pinMode(GPIO2, INPUT_PULLUP); // sets pin as input  
  pinMode(GPIO3, INPUT_PULLUP); // sets pin as input  
  pinMode(GPIO4, INPUT_PULLUP); // sets pin as input  

}
/* -----------------------------------------------------------------------------
  loop()

 -----------------------------------------------------------------------------*/
void loop()
{

  checkState();
  delay(500);
  
}

This is how I did it.
Thanks @peekay123, another great way to do it.

1 Like

Man your fast @peekay123. Thank you so much. I will order a dip switch and try it out on the breadboard.

Thank you, Thank you, Thank you. You have helped me so many times on my project I have lost count. I think I need to send you a bottle of wine.

2 Likes

Thank you @seulater. This looks like a great way as well. I appreciate you taking the time to comment and share.

@peekay123, is quite a bit shorter. Just know there are left out gotchas tn both cases.
Such as a combo of switches set. Say 1,3 & 5 at the same time. So you need to handle those types of situations.

Dip switches are good for this and are fairly cheap. An alternative you might also consider is a rotary encoded switch although a bit more costly. The advantage is that each position on the rotary switch correlates to a different type of flow meter. If you ask a customer to set the DIP switch, a non-savvy person is more prone to make a mistake setting multiple DIP switches rather than setting a single rotary switch to a specified position (my opinion). The rotary switches can output either a BCD, hexadecimal, or single pin for each position depending on the model you order.

2 Likes

Yea this one is going to be a head scratcher for me. I keep coming up with ideas that are way ahead of my learning curve. :joy:

I still need to figure out how to even wire this for testing. Having never worked with Dip switches I think I should have stared there first. lol. Is it as simple as the picture below running the pins to the dip then to the ground or do I have it backwards? Run 3.3v through the dip to the B# pins to trigger them based on configuration?

Note I just did a quick search and copied that pic off the web just for the general idea.

That is a great idea. It would make it much easier for the client and for any for anyone setting up the unit before it left the office.

You got it.

1 Like

@GasGen, is this a factory setting or a client setting? If you expect the client to set this switch then you will need to be careful and include a disclaimer. Though the rotary switch suggested by @ninjatill is a great idea, getting the wrong setting will fundamentally affect the device’s operation. So what switch you use is very dependent on HOW it will be used and configured.

As for the dip switches, you diagram shows switches going to GND with your code configuring the inputs as INPUT_PULLUP. A switch that is “ON” will cause the corresponding pin to go LOW. Another way is to have one side of the swiches go to 3V3 and the inputs configured for INPUT_PULLDOWN. That way, a switch that is “ON” will cause the corresponding input to go HIGH.

1 Like

I assume it will be done at the office. But that could change. It would be nice to offer the device as field configurable so that the units can be installed on any size flow meter we offer. Or changed at a later date to a different size.

Based on your code suggestion above then I assume I should run 3v3 to the dip and then to the B# pins?

Or you could save the cost of the switch entirely, and have them plug the USB into the PC and you can put up a menu. Or have it act as an AP, and serve up a webpage for configuration which is what I do.

2 Likes

Or have them bring it online and do a Particle.publish() to set the configuration. Lots of possibilities. I am doing the rotary switch thing to configure I2C addresses for a modular relay panel project. I have to set the I2C address before connecting the panel to the modular system.

2 Likes

Thats a very interesting idea as well. This is one of the reasons I love this board… all the great ideas and suggestions.

@seulater Have you any examples of using the micro USB connector to provide a ‘port’ to a PC and putting up a menu? I presume that you would need the PC user to run a serial terminal and write and read to Serial or were you thinking of something else?

Maybe the word "menu" overstates it.

In loop() periodically check for anything received on the serial port.

	// Did the user at the Terminal want to do something ?
	if(Serial.available())
	{
		menu();
	}
/* -----------------------------------------------------------------------------
  menu()

  Notes:  Send commands through the terminal to do special things
 -----------------------------------------------------------------------------*/
void menu(void)
{
    // Purge Serial port
    while(Serial.available() >0 )
    {
	Serial.read();
    }

    Serial.println("Choose an Option");
    Serial.println("A) Enter Diagnostics");
    Serial.println("B) Change water level");
    Serial.println("C) Calibrate Sensors");
    Serial.println("Q) Quit menu");

    // Wait here until they select something
    while(!Serial.available()) Particle.process();

// Now read serial, and do what the user has selected. 
}

3 Likes

@seulater, thanks for that example!

I assume the general concept is to write changes to EEPROM, and read the EEPROM during Setup() for the changes ?

I apologize for what a lot of people might consider a silly question, but this thread has opened my eyes to many different ways to accomplish something that’s always bothered me.

Thanks to everybody.

What I do on boot are two main things.
#1) Show info about the firmware.

	Serial.printf("\r\n************************************\r\n");
	Serial.printf("System Boot - ");
	Serial.printf("sv: %s",	System.version().c_str());
	Serial.printf(" - %s", FIRMWARE_REV);
	Serial.printf("\r\n************************************\r\n");

Then Like you suggested I read the EEPROM and get my saved values using EEPROM.get().
Among my data that is stored in EEPROM i put a "EEPROM_MAGIC_NUMBER" in there as well.
#define EEPROM_MAGIC_NUMBER 0xc3c3
I use this to know if this unit is fresh off the line, or has been run before. If there is no MAGIC_NUMBER, then I know I need to set all my values to their factory defaults for the user.

/* -----------------------------------------------------------------------------
  getEEpromInfo()

  Notes:  Check EEprom, and get saved values
 -----------------------------------------------------------------------------*/
void getEEpromInfo(void)
{
	// Wipe the EEPROM clean, I.E. Fresh Start
	//    EEPROM.clear();

 	// Read EEprom
 	EEPROM.get(0, myEEprom);

 	// Check to see if we have been programmed before
	if(myEEprom.ee_flashed != EEPROM_MAGIC_NUMBER)
 	{
	 	Serial.printf("\r\n!!! EEprom NOT Flashed Yet, Setting Defaults...\r\n\r\n");

	 	// Lets Set The Magic number
	 	EEPROM_Object temp_object = { EEPROM_MAGIC_NUMBER };

	 	// Now save them
	 	EEPROM.put(0, temp_object);

		// Since this is first boot, lets set the defaults.
		setDefaults();

		System.reset();
 	}
 	else
 	{
	 	Serial.printf("EEPROM, MAGIC NUMBER OK...\r\n");
 	}
}
2 Likes

Well I have been fighting my way through this and got stuck. Amazingly I was able to get the code to compile on my Photon… I test on the photon before moving over to my electrons. I use pins D2 and A2, put two switches on the breadboard and ran 3v3 to the pins. When both switches are off I get normal results… or what I mean is the data from the first row of meter look up: { 2139.0, 10614.0, 0.0, 200.0 },
Now if I flip the switch and send power to Pin D2, I don’t get the first row calculation but the second row. { 1000.0, 8000.0, 0.0, 40.0 }

Now if I switch off D2 and Power A2 I get the ADC value then some really long number ADC value. See the image of the console.

What the heck did I do wrong here?
Thank you in advance for your time.
Cheers,
Tom

Switch Wiring: Orange is 3v3, Blue is going to D2, Red is going to A2

Simpified code for posting purposes.

int dip;
float ma = 0.0;
float flow = 0.0; 
float adc =0.0;

void setup() {
inputBoard.setAddress(0);
    
pinMode(D2, INPUT_PULLDOWN);
pinMode(A2, INPUT_PULLDOWN);
}

void loop() {
int16_t input_1 = inputBoard.readInput(1);
ma=(input_1); //ACTUALLY THIS IS ADC VALUE NOT MA
adc=(input_1); //ACTUALLY THIS IS ADC VALUE 
dip = 0;
if (digitalRead(D2)) dip+=1;
if (digitalRead(A2)) dip+=2;
// if (digitalRead(B4) dip+=4;
// if (digitalRead(B5) dip+=8;

struct meter_lookup {
  float fromLow;
  float fromHigh;
  float toLow;
  float toHigh;
};
const meter_lookup meters[2] = {
 { 2139.0, 10614.0, 0.0, 200.0 },
 { 1000.0, 8000.0, 0.0, 40.0 }
// { 1000.0, 8000.0, 0.0, 40.0 },  // whatever values for third meter type
// { 1000.0, 8000.0, 0.0, 40.0 }   // whatever values for fourth meter type
};
float flow = map(adc, meters[dip].fromLow, meters[dip].fromHigh, meters[dip].toLow, meters[dip].toHigh);
String F = String::format("%.1f", flow);

if (Particle.connected() && millis()-lastpub > 60000) {
		lastpub = millis();
Particle.publish("GGS", 
                "{\"cf\":\"" + F + 
               "\"}", 60, PRIVATE, NO_ACK); // NOTE Added NO-ACK to save data. 
}