Pulse olfactometer

Hello all,

I’m a hardware person at heart, and have recently been getting back into software to develop a piece of scientific apparatus. The field is in perceptual psychology/ olfaction and I’m developing a new type of device called a pulse olfactometer.

Essentially, a pump fills two chambers with smell, and then a valve array distributes the air to right and left nostrils. Where I work as a research assistant, we’re hoping to use the device to discover new things about how our sense of smell works (for instance, suppose that I pump a strawberry odour into your left nostril, and lemon to your right - would you notice if I briefly reversed the polarity?) Here’s what the UI looks like:

Introductions aside, I’m having some difficulty programming even the basic functions because my knowledge is pretty poor. The basic operation is like this (I’ll paste what code I have later, but for now just in “arduinenglish”)

if (begin button pressed) // button widget
turn on (pump)
delay(fill duration) // value from numerical input widget
turn off (pump)
delay (release delay) // value from numerical input widget
turn on (some selection of valves) // based on which valves selected in app
delay (duration) // how long the selection of valves left open, from app numerical input
turn off (all valves)

if(reset button pressed)
abort sequence and wait for begin button again

Hardware model: ESP8266/ NodeMCU12E +wifi
Android Oreo with Blynk app
• Blynk server
• Blynk Library downloaded a few days ago

I know that this is a relatively simple project, but I don’t know how to use the value from the numerical input as the fill duration, and then how to set that as the delay between turning the pump on and off again.

Here is my code so far (sadly not much, I know), I’ve taken out the bits I know to be wrong (not sure if that’s helpful):


#define BLYNK_PRINT Serial


#include <ESP8266WiFi.h>
#include <BlynkSimpleEsp8266.h>

// You should get Auth Token in the Blynk App.
// Go to the Project Settings (nut icon).
char auth[] = "YourAuthToken";

// Your WiFi credentials.
// Set password to "" for open networks.
char ssid[] = "YourNetworkName";
char pass[] = "YourPassword";

void setup()
{
  // Debug console
  Serial.begin(9600);

  Blynk.begin(auth, ssid, pass);
  // You can also specify server:
  //Blynk.begin(auth, ssid, pass, "blynk-cloudcom", 80);
  //Blynk.begin(auth, ssid, pass, IPAddress(192,168,1,100), 8080);
}

const int pump = 7;
const int valve1 = 17; 
const int valve2 = 18; 
const int valve3 = 20; 
const int valve4 = 4; 

long fillduration;
int releasedelay;
int duration;

pinMode(pump, OUTPUT); // Sets the pump as an output
pinMode(valve1, OUTPUT); // Sets valve1 as an output
pinMode(valve2, OUTPUT); // Sets valve2 as an output
pinMode(valve3, OUTPUT); // Sets valve3 as an output
pinMode(valve4, OUTPUT); // Sets valve4 as an output




void loop()
{
  Blynk.run();
}


BLYNK_WRITE(V3) { // pump fill duration input in app
  fillTime = param.asInt();

BLYNK_WRITE(V4) { // delay between pmp stopping and valves opening
  releasedelay = param.asInt();

BLYNK_WRITE(V5) { // valve 1 selection switch
  fillTime = param.asInt();

// and so on


}

Thanks so much and of course I would be happy to give anyone that helps a mention in any academic papers which this device is used to collect data for (if they so wish!)

Many thanks

Harry

Here’s the hardware side:

Couple of threads for you:

These numbers (7, 17, 18 etc) should represent the General Purpose Input/Output (GPIO) pins of the device that you’re using. In your case, you’ve said that this is a NodeMCU.
I’m guessing that you’ve found this code from something that was written for an Arduino Uno or Mega, which have more GPIOs available for use than the NodeMCU that you’re using.

To confuse you even further, the NodeMCU has “D” numbers screen printed next to the pins, which don’t correspond to the GPIO number of the pin, and some of the NodeMCU pins aren’t suitable for controlling relays etc because they behave in a particular way when the device is booted.
Take a look at this post:

and a pinout chart for your device like this:

Also, this code wont compile correctly, becaise you’re missing some closing curly brackets.

BLYNK_WRITE(V3) { // pump fill duration input in app
  fillTime = param.asInt();

BLYNK_WRITE(V4) { // delay between pmp stopping and valves opening
  releasedelay = param.asInt();

BLYNK_WRITE(V5) { // valve 1 selection switch
  fillTime = param.asInt();

I’d reccomend that you lay out your curly brackets differently, so that the code is more readable:

BLYNK_WRITE(V3) // pump fill duration input in app
{
  fillTime = param.asInt();
}

BLYNK_WRITE(V4)  // delay between pmp stopping and valves opening
{
  releasedelay = param.asInt();
}

BLYNK_WRITE(V5)  // valve 1 selection switch
{
  fillTime = param.asInt();
}

The variable names you used in the above code don’t match those that you’ve declared at the top of your code:

long fillduration;
int releasedelay;
int duration;

and you’ve used fillTime in both your V3 and V5 BLYNK_WRITE functions, presumably because you copied and pasted and didn’t change the variable name afterwards.

Pete.

1 Like

Hi Pete,

Thanks so much for your detailed reply - I realize now that there are so many errors in that code.

Do you have any advice about my major issue: getting the numerical input to determine the fill delay?

Many thanks

Harry

Well, once you’ve sorted-out the code to the point where it will compile, which will include making the amendments I pointed-out, you’ll have those values you need coming from the widgets on V3, V4 & V5.

However, you don’t yet have a button widget attached to a virtual pin to trigger your process. Let’s assume that you attach this to V6.

When the V6 button is pressed/released, it will trigger a BLYNK_WRITE(V6) event and the result will be a 1 (pressed) or a 0 (released).
You’ll use an If statement within your BLYNK_WRITE(V6) function to determine which it was, and then take the appropriate action.

Take a look at the code that @Costas linked for more info.

Personally, I think you’d be better allocating pre-defined values to your pump run variables to begin with, rather than using the values that come from your V3, 4. & 5 widgets as this will make your debugging easier initially.
You should also use plenty of Serial.print statements to show you what’s happening with your program flow and your variable values along the way.

It will be a fairly steep learning curve to get to the point where you can get the code to work the way you want, but experimentation is the best way to learn. Those Serial.print statements will help you to see what’s happening, but if you struggle to make sense of what’s happening the play with some of the simpler examples in Sketch Builder first.

Pete.