Trouble moving analog values

So I have a vermicomposter in my basement that we put all of our food scraps in . It was drying out so I made an automatic misting system for it to keep it moist. The first code I had worked ok, although I know it was not optimal. I threw it together quickly and let it go along nicely for two years. Here is that code.

int sensor0Pin = A3;   
int sensor1Pin = A5; 
int solenoidWaterPin = 8;        
int soil0 = 0;       
int soil1 = 0;      
const int highThreshold = 825;
const int dryestReading = 625;   
const int overSaturation = 400; 
const long sprayInterval = 20000;         
const long waitInterval = 120000;          
const long serialWriteInterval = 3000;      
unsigned long previousMillis = 0;  
unsigned long lastUpdate = 0;     
boolean thresholdSpray = false;



void setup()                 
 {Serial.begin(9600);
 

 pinMode(solenoidWaterPin,OUTPUT);
 digitalWrite(solenoidWaterPin, HIGH);}


void loop() 
 
  {int sensor0Value = analogRead(sensor0Pin);  // Read the value of the front moisture sensor.
   int sensor1Value = analogRead(sensor1Pin);   // Read the value of the rear moisture sensor.
   sensor0Value = constrain(sensor0Value, 300, 1023);
   sensor1Value = constrain(sensor1Value, 300, 1023);
   unsigned long currentMillis = millis();

  
  //Map the value to a percentage
  soil0 = map(sensor0Value, 300, 1023, 100, 0);
  soil1 = map(sensor1Value, 300, 1023, 100, 0);
 

  if (currentMillis - lastUpdate >= serialWriteInterval) {
                 lastUpdate = currentMillis;
       Serial.print("Soil saturation at front is ");
       Serial.print(soil0);
       Serial.println("%");
       Serial.print("Soil saturation at rear is ");
       Serial.print(soil1);
       Serial.println("%");
       Serial.println("");
       Serial.print("Front Sensor = ");
       Serial.println(sensor0Value);
       Serial.print("Rear Sensor = ");
       Serial.println(sensor1Value);
       Serial.println("");
       Serial.println(thresholdSpray);
   
                                                         }
  // If the analog value is high enough, turn on the solenoid relay. If we have sprayed, wait for water to saturate before allowing another spray.  
  if (sensor0Value < overSaturation || sensor1Value < overSaturation || sensor0Value > highThreshold || sensor1Value > highThreshold) 
      {digitalWrite(solenoidWaterPin,HIGH);
      }
    else if (thresholdSpray == false && (sensor0Value > dryestReading || sensor1Value > dryestReading) && (currentMillis - previousMillis >= waitInterval))
             { digitalWrite(solenoidWaterPin, LOW);
               previousMillis = currentMillis;
               thresholdSpray = true;
             }
                                                           
    else if (thresholdSpray == true && (currentMillis - previousMillis >= sprayInterval))
             {digitalWrite(solenoidWaterPin,HIGH); 
              previousMillis = currentMillis;
              thresholdSpray = false;                    
             }
       }   

 // (sensor0Value > highThreshold or sensor1Value > highThreshold)

So in the serial monitor I would get values in the 600- 700 range when the soil was sort of drying out. Below that when sufficiently saturated and above that when dry. I’ve changed the code in order to update a google spreadsheet so I can keep an eye on things remotely. I’ve tried to put everything in its own function, but it’s not working correctly. So to the crux of my problem. With the following code the analog value always returns 300. I can reload my old sketch and the values read in the 650 range. I have tried moving the declaration into different sections including global scope. I’ve been trying to learn about references, pointers and functions, but I am not finding a whole lot of info. Especially for Ardiuino. And tips would be appreciated. I ran out of room so I will post my new code in the post after this. Thanks

My new code.

------------------------------------------------------------------------------
*/
#define W5200_CS  10
// SD card SPI CS pin
#define SDCARD_CS 4
#include <SPI.h>
#include <EthernetV2_0.h>
//------------------------------------------------------------------------------
int energizeSensors = 7;                     
int solenoidWaterPin = 4;                     
int sensor0Pin = A0;                         
int sensor1Pin = A1;                        
//int frontSensor = 0 ;                    
//int rearSensor =  0;                       
int soil0 = 0;                              
int soil1 = 0;                             

int sprayCount = 0;                          
int connectionFailureCount=0;            
const int highThreshold = 825;                
const int dryestReading = 560;                
const int overSaturation = 525;           
const long sprayInterval = 15000;            
const long waitInterval = 5000;              
const long ethernetWriteInterval =15600;    
const long serialWriteInterval = 3000;        
unsigned long lastSerialUpdate = 0;          
unsigned long lastEthernetUpdate = 0;       
unsigned long previousMillis = 0;            
unsigned long lastSensorRead = 0;            
    
boolean sprayInitialize = false;            
boolean needMoisture = false;               
const char website[] = "api.pushingbox.com"; 
const String devid = "#*#*#*#*#*#*#*";     
byte mac[] = {0xDE,0xAD,0xBE,0xEF,0xFE,0xED}; 

//------------------------------------------------------------------------------

void setup() 
{
  Serial.begin(9600);
  Serial.println("Begin setup.");
   // make sure that the SD card is not selected for the SPI port
   // by setting the CS pin to HIGH
   pinMode(SDCARD_CS,OUTPUT);
   digitalWrite(SDCARD_CS,HIGH);
   //Initializing pin 8 as output. 
   pinMode(solenoidWaterPin,OUTPUT);
   pinMode(energizeSensors, OUTPUT);
   digitalWrite(solenoidWaterPin, HIGH);
   digitalWrite(energizeSensors, HIGH);


   // loop until we get a connection to the ethernet
   Serial.println("Looking for ethernet connection.");
   while(Ethernet.begin(mac)==0)
    {  Serial.println("DHCP configuration failed. Trying again...");
       Ethernet.begin(mac);
    }
     Serial.println("DHCP configured.");
}

//------------------------------------------------------------------------------

void loop()
{  
  int frontSensor = analogRead(sensor0Pin);  // Read the value of the front moisture sensor.
  int rearSensor = analogRead(sensor1Pin);   // Read the value of the rear moisture sensor. 
    unsigned long currentMillis = millis();
        
  frontSensor = constrain(frontSensor, 300, 1023);
  rearSensor = constrain(rearSensor, 300, 1023);
    


    
   if (currentMillis - lastSerialUpdate >= serialWriteInterval) 
   {   lastSerialUpdate = currentMillis;
       Serial.print("Soil saturation at front is ");
       Serial.print(soil0);
       Serial.println("%");
       Serial.print("Soil saturation at rear is ");
       Serial.print(soil1);
       Serial.println("%");
       Serial.print("Front Sensor = ");
       Serial.println(frontSensor);
       Serial.print("Rear Sensor = ");
       Serial.println(rearSensor);
       Serial.print("Spray count = ");
       Serial.println(sprayCount);
       Serial.print("Connection failure count = ");
       Serial.print(connectionFailureCount);
       Serial.println("");
   }   
 
   if (currentMillis - lastEthernetUpdate > ethernetWriteInterval) 
   { lastEthernetUpdate = currentMillis;
     Serial.println("In update loop.");
     digitalWrite(energizeSensors, LOW);
     sensorRead();
     updateSpreadsheet();
     digitalWrite(energizeSensors, HIGH);     
  
   }

}                                    

//------------------------------------------------------------------------------

void sensorRead(){

       int frontSensor = analogRead(sensor0Pin);  // Read the value of the front moisture sensor.
       int rearSensor = analogRead(sensor1Pin);   // Read the value of the rear moisture sensor. 
   // Remove the bottom 300 points.
       frontSensor = constrain(frontSensor, 300, 1023);
       rearSensor = constrain(rearSensor, 300, 1023);
   //Map the value to a percentage
       soil0 = map(frontSensor, 300, 1023, 100, 0);
       soil1 = map(rearSensor, 300, 1023, 100, 0);

       unsigned long currentMillis = millis(); 
       Serial.println("Energizing sensors for reading");
       Serial.println("");


while (millis() - currentMillis < waitInterval) 
   {   Serial.println("Waiting for sensors to stabilize"); 
       Serial.println(frontSensor);
       Serial.println(rearSensor);
   }      
   // If the analog value is high enough, but not too low, turn on the solenoid relay. If we have sprayed, wait for water to saturate before allowing another spray.  

       Serial.println("Now reading moisture sensors");
       Serial.println(frontSensor);
       Serial.println(rearSensor);
   if ((frontSensor > dryestReading || rearSensor > dryestReading) && (frontSensor > overSaturation && rearSensor > overSaturation)) 
   {   Serial.println("Soil needs moisture");

       needMoisture = true;
       saturate();
   } 
   else if (((frontSensor < dryestReading || rearSensor < dryestReading) && (frontSensor > overSaturation && rearSensor > overSaturation))||(frontSensor < overSaturation || rearSensor < overSaturation)) 
   {   Serial.println("Soil does not need moisture");
       needMoisture = false; 
       digitalWrite(energizeSensors, HIGH);
   }
   return frontSensor;
   return rearSensor; 
}
//------------------------------------------------------------------------------

void updateSpreadsheet(){
  Serial.println("Initializing Ethernet connection");
  Serial.println("");
 // delay(2000);
  
  int frontSensor = analogRead(sensor0Pin);  // Read the value of the front moisture sensor.
  int rearSensor = analogRead(sensor1Pin);   // Read the value of the rear moisture sensor. 

// Remove the bottom 300 points.   
  frontSensor = constrain(frontSensor, 300, 1023);
  rearSensor = constrain(rearSensor, 300, 1023);
//Map the value to a percentage
  soil0 = map(frontSensor, 300, 1023, 100, 0);
  soil1 = map(rearSensor, 300, 1023, 100, 0);

  EthernetClient client; //define 'client' as object
     
  //Start or API service using our EtherNet Client through PushingBox
    if (client.connect(website, 80)) 
    {
    
        client.print("GET /pushingbox?devid=" + devid
       + "&frontSensor="    + (String) frontSensor
       + "&rearSensor="     + (String) rearSensor
       + "&sprayCount="     + (String) sprayCount 
//       + "&hicData="      + (String) hicData
//       + "&hifData="      + (String) hifData 
                                                  );

      client.println(" HTTP/1.1"); 
      client.print("Host: ");
      client.println(website);
      client.println("Connection: close");
      client.println();                                  
      Serial.println("Spreadsheet updated.");
      Serial.println("");
    }
    
   else
   {
     Serial.println("Ethernet connection failed");
     Serial.println("");
     connectionFailureCount++;
   }  
   return frontSensor;
   return rearSensor;    
}

//------------------------------------------------------------------------------

void saturate() 
  {
    Serial.println("Begin saturation routine");
    unsigned long startSprayTime = millis();
  
   do  {   digitalWrite(solenoidWaterPin, LOW);
           Serial.println("Now spraying");
           Serial.println(""); 
       }
   while (millis() - startSprayTime < sprayInterval);
            
             
     digitalWrite(solenoidWaterPin, HIGH);
      Serial.println("Spray complete");
      Serial.println("");
     
 sprayCount++;
  }

First code:

int sensor0Pin = A3;   
int sensor1Pin = A5;

Second code:

int sensor0Pin = A0;                        
int sensor1Pin = A1;

Hmm?

When you say the "analog value always returns 300", are you checking that value before or after the constrain? If you've miswired something when you rearranged pins and the sensors are reading 0, the constrain will change that to 300. I don't think it's a coincidence that the number you report is the same as one of your constrain limits.

     digitalWrite(energizeSensors, LOW);
     sensorRead();
     updateSpreadsheet();
     digitalWrite(energizeSensors, HIGH);

A schematic is also necessary for how you are energizing the sensors, and what sensors they are. I assume it’s probably one of those soil moisture sensors that’s really just a resistance probe. If it’s not actually active low energizing, you’re probably reading 0 from the ADC, which gets constrained to 300.

Hi,

Can I suggest for trouble shooting you replace the moisture sensors with potentiometers, so that you can manually change the two analog inputs.

A copy of your circuit, in CAD or a picture of a hand drawn circuit in jpg, png would be helpful.

Tom... :slight_smile:

A3 and A5 did indeed get moved to A0 and A1. Sorry, I forgot to mention that change. Good catch!

I use the 5V out from the Arduino to power a terminal block. Energizing pin 7, closes a relay allowing the 5V to power the moisture sensors. This is confirmed working just fine. The leds light up on the relay board, the relay clicks, 5V checked and leds on the sensor board.

For sensors I use the cheap-o soil sensors with A-D converter, only there is also an analog pin. I've removed the cheap-o sensor probes and made my own out of stainless steel. They were in the the original design though, so nothing has changed there except I power them through the relay.

I think you are right that it is not a coincidence. Your hypothesis is mostly sound, however I don't think it is 0v. After i left them on for an hour or so I would get 301, 302. Even as high as 312 eventually. (It's almost as if they warm up) I made sure to move the pins correctly and double and triple checked.

With the constrain removed I am getting ~54 on one and ~156 on the other. I put my old code in and they read ~660.

If you think my code is sound I will continue troubleshooting the mechanical side, but I am pretty certain everything is copacetic. This thought is reinforced by the fact that my old code returns familiar readings. I thought there was maybe a funky way that analog values were passed between functions that was causing me trouble. I think I will write a test code where I only use the loop() and see what readings I am getting. If that doesn't work I will try potentiometers. I wish I understood more about C programming. I'm sure there is a better way to do this, but I am used to ladder logic and PLCs. This should be a relatively mindless task, but I am just not familiar enough. Thanks a bunch for your help, guys.

OK so here are my findings.

I created a very simple sketch with only the analogRead and the digitalWrite to turn the sensors on. I purposely turned them off in setup() and then turn them on the first time through the loop. I removed all constrains and scaling. I delay for 3 seconds after turning them on. I never turn them off. The first reading I get was ~60 and ~150, The second time through and every time after that they read 650-710. Perfect!

So I went back to my code, remove my constrains and make all my digitalWrites to Low (backwards, but that's how my relays work) so the sensors are always on. I then open the serial monitor and have a look. 54, 160. the next loop, 655, 720. If I allow them to go off the don't work. What's interesting is that I don't think the sensor has to warm up per se, because I've run them from anywhere from 3 seconds to 10 seconds. It's not the time "on" that they need, its going through one loop energized and beginning the next loop in the same state as the last. I've confirmed this by turning them off at the end of the loop and delaying. Does that make sense? Now why would that be?

One other interesting thing. My serial monitor prints out a lot of values over the 15 seconds or so that is reading the sensors. The values never change while printing. Only the next loop when it prints will they be different.

Anyway at least I feel like I've made some progress. Thanks again for all your help.

Does that make sense?

In the absence of the code, not a bit.

You could call analogRead() in setup(), and discard the first reading, if it really is problematic.

For sensors I use the cheap-o soil sensors with A-D converter, only there is also an analog pin. I've removed the cheap-o sensor probes and made my own out of stainless steel. They were in the the original design though, so nothing has changed there except I power them through the relay.

That is not a good enough description. Soil moisture sensing is not something that I have ever done so I have 0 familiarity with it, I've just seen sensors for it on ebay. I haven't looked into if there are different kinds or not. At a minimum, you should give the manufacturer+part number or a link to the product page where you bought it from.

And since you've DIYed your own, I need to know exactly how you're hooking those nails up to the circuit.

What's interesting is that I don't think the sensor has to warm up per se, because I've run them from anywhere from 3 seconds to 10 seconds. It's not the time "on" that they need, its going through one loop energized and beginning the next loop in the same state as the last. I've confirmed this by turning them off at the end of the loop and delaying. Does that make sense? Now why would that be?

I believe it does. It sounds like the problem is not a warm up time for the sensor, but inadequate drive for the ADC.

If it's similar to this sensor from Sparkfun, it should have a transistor in the circuit buffering the drive current. Does your design?

It is similar to the sparkFun sensor. Everything is the same except I replaced the chincy plastic fork with stainless steel probes. I’m certain there is nothing wrong with the sensor, though. Certain.

What I’ve done is made a very simple sketch to demonstrate what is happening. With pin 9 connected to A0 I would think you would get a value of 1023 on A0.

If I declare int analogValue = analogRead(sensor0Pin) globally, Analog value = 0 is printed 10 times.

If declare as soon as I get into the readAnalog() function, I get a value that will not change when I remove the wire in the middle of the For loop. Like it’s frozen. If I move my wire from pin 9 to 3.3V i get 675 next time through the loop, but it will NOT change mid loop. If it is at 546 when it enters the For loop it prints that 10 times, even if I change the voltage mid loop.

If I move it inside the For loop it works and changes dynamically if I remove the wire mid loop.

I can kind of see how this makes sense in regards to the for loop, but I don’t understand why I don’t see a dynamic change when I call a function. For instance if a declare an analog variable globally and then write it high with a digital pin, delay 3 seconds, and then call a function that prints the value, it is zero. I don’t understand that. Where does the voltage go? It still has 5 volts on it when it in the function. Why does it read zero? And as always, thanks for the help.

int enableReading = 9;                                       
int sensor0Pin = A0;                       
//*******Move here and it will always be 0***********


//------------------------------------------------------------------------------

void setup() 
{
  Serial.begin(9600);
  Serial.println("Begin setup.");
  pinMode(enableReading, OUTPUT);
  digitalWrite(enableReading, LOW);
}

//------------------------------------------------------------------------------

void loop()
{  digitalWrite(enableReading, HIGH);
   delay(3000);
   readAnalog();
   digitalWrite(enableReading, LOW);
   delay(3000);
}

//------------------------------------------------------------------------------

void readAnalog()
{   //*********Move here and it will display a frozen value.*************
for (int Count = 0; Count < 10; Count++)

    {  int analogValue = analogRead(sensor0Pin); //*******Move here and it works as expected.*****
       Serial.print("Analog value = ");
       Serial.println(analogValue);
       Serial.println(""); 
       delay(250);
    }
}                                    


//------------------------------------------------------------------------------

Hi,
In your program you do this to read, please try this;

void loop()
{  
 int frontSensor = analogRead(sensor0Pin);  // Read the value of the front moisture sensor first time.
 frontSensor = analogRead(sensor0Pin);  // Read the value of the front moisture sensor second time.

  int rearSensor = analogRead(sensor1Pin);   // Read the value of the rear moisture sensor first time.
   rearSensor = analogRead(sensor1Pin);   // Read the value of the rear moisture sensor second time.

   unsigned long currentMillis = millis();
      
 frontSensor = constrain(frontSensor, 300, 1023);
 rearSensor = constrain(rearSensor, 300, 1023);

Add the second analogReads.

Due to the fact that there is only one AtoD, when you read analog inputs the AtoD is switched to the relevant input pin when an analogRead is called.
The AtoD has a capacitor on its input that has to charge to the pins voltage, this takes time, more time than a consecutive analogRead takes.
So reading the same analog input twice and using the last reading ensures that the capacitor has charged to that pins voltage.

Tom... :slight_smile:
Can you please post a copy of your circuit, in CAD or a picture of a hand drawn circuit in jpg, png?

I’ve included a crude, hand-drawn schematic.

Hmmm. I’m a little skeptical that this is a capacitor charge issue, for this reason.
If i serial print an analogRead with a 2ms delay between prints I can watch the value change all across the range with something like a potentiometer or even a PWM pin fading up and down. Every single value will change every 2 ms. Even if I set the pin to ground and then to high it’s extremely reactive. In my code I set the pin HIGH wait 3 seconds and still get zero.

Unless I am misunderstanding what you mean, i think this has more to do with scan order and scope. I’m just not familiar enough. I think we are making progress though. Thanks again.

Hi,
Thanks for circuit.
04807dac6cb10c63e7f7e86343af42ac954b98b1.jpg
Tom… :slight_smile:

Hi,

Why are the 5+ supply wires coming from the relays, not from the +5V?

Tom... :slight_smile:

I intitially had them coming straight out of the Arduino, but when they didn't work I thought maybe an output pin couldn't source as much current as the 5V out. I had an extra relay and I was grasping at straws.

Have you tried my test sketch in post #9? If you look at the commented lines, it will show you three different places to put " int rearSensor = analogRead(sensor1Pin); ". You get very different results when you look at the serial monitor. Play around with removing the wire in the middle of the loop and see how it behaves differently in different areas. It demonstrates the same issue I am having. I'm just not quite sure how to solve it.

Hi,
Your code posted in #9, works fine for me, I used a pot for the analog input.
Can you post a link to your sensor spec/data thanks?

Tom... :slight_smile:

Hi,

If i serial print an analogRead with a 2ms delay between prints I can watch the value change all across the range with something like a potentiometer or even a PWM pin fading up and down.

That is with one channel reading only, if you have two channels with different voltages on them you can see a difference between reading each pin once or twice consecutively.

There is much less than 2mS between two lines of code

int frontSensor = analogRead(sensor0Pin);  // Read the value of the front moisture sensor.
  int rearSensor = analogRead(sensor1Pin);   // Read the value of the rear moisture sensor.

Tom.. :slight_smile:

What are you supposing this code does?

   return frontSensor;
   return rearSensor;

hillm5714:
My new code.

Which doesn't compile:

/tmp/arduino_modified_sketch_793104/sketch_dec14a.ino: In function 'void sensorRead()':
sketch_dec14a:146: error: return-statement with a value, in function returning 'void' [-fpermissive]
    return frontSensor;
           ^
sketch_dec14a:147: error: return-statement with a value, in function returning 'void' [-fpermissive]
    return rearSensor;
           ^

hillm5714:
I can kind of see how this makes sense in regards to the for loop, but I don’t understand why I don’t see a dynamic change when I call a function. For instance if a declare an analog variable globally and then write it high with a digital pin, delay 3 seconds, and then call a function that prints the value, it is zero. I don’t understand that. Where does the voltage go? It still has 5 volts on it when it in the function. Why does it read zero? And as always, thanks for the help.

int enableReading = 9;                                       

int sensor0Pin = A0;                     
//Move here and it will always be 0****

//------------------------------------------------------------------------------

void setup()
{
  Serial.begin(9600);
  Serial.println(“Begin setup.”);
  pinMode(enableReading, OUTPUT);
  digitalWrite(enableReading, LOW);
}

//------------------------------------------------------------------------------

void loop()
{  digitalWrite(enableReading, HIGH);
  delay(3000);
  readAnalog();
  digitalWrite(enableReading, LOW);
  delay(3000);
}

//------------------------------------------------------------------------------

void readAnalog()
{  //Move here and it will display a frozen value.****
for (int Count = 0; Count < 10; Count++)

{  int analogValue = analogRead(sensor0Pin); //**Move here and it works as expected.
      Serial.print(“Analog value = “);
      Serial.println(analogValue);
      Serial.println(””);
      delay(250);
    }
}

//------------------------------------------------------------------------------

I think I see the problem now. You don’t have a proper understanding of how this stuff works.

The ADC hardware in the Arduino microcontrollers does not perform continuous conversion of the analog value applied to the input, you have to manually start the conversion process and wait for it to finish. And it is an actual process that takes a fair bit of time (approximately 100 us). There is one type of ADC that can work instantly (flash conversion), but it is expensive to implement on a chip. Most microcontrollers use the much easier successive approximation method.

The primitive variable types in C++ only hold a static value, you can’t bind a reference to some function or hardware to them. You can fake that kind of effect with an advanced class that transparently calls analogRead every time its value is accessed, but a normal int won’t do that. int just holds a number, so when you do something like int reading = analogRead(A0);, the reading variable will contain the same value forever, and will not change until you assign a new value to it (such as with a second analogRead).

Every time you need to check the analog value again, you need to do another call of analogRead.

If you do the analogRead once in global scope and never again, of course the value is never going to change. You’re never assigning a new value to the variable.

When you put it in the second spot, before the for loop, it freezes the value because the for loop never changes it. Even if you change the voltage on the pin while the for loop is running, you don’t do another conversion in the for loop. It’s not that the value is “frozen”, you are that one that’s not checking it and are just reusing the old value.

It works in the 3rd spot because then you are doing a fresh conversion every time you need to check the pin for its current voltage. That is the proper way of doing it.

Looking back at the code you posted in reply #1, I’m wondering if the problem might be that you you’re messing up the sequence of energizing and de-energizing the sensors. It’s difficult to follow your logic since it’s spread all over the place though. I notice that there is one branch condition in readSensors that actually shuts the sensors off before updateSpreadsheet runs. Then when you read them in updateSpreadsheet they’ll be off and you’ll get a super-low value.

That code’s a mess, and it’s hard to tell what’s going on without a thorough analysis. It looks like you’ve been able to make some of the individual stuff work like updating the spreadsheet and hydrating the soil, now you just need to collect those thoughts into a coherent plan.

Step away from the code, get out some dead tree paper and write out a plan for your code. Define what you want to do in what conditions, such as “if the soil is below some level of dryness (below 400 ADC reading or something), do XXX (turn on the solenoid or whatever)” and “every X amount of time, send an update to the spreadsheet” and whatever else you want to do. Try to organize them into a sensible sequence of operations “energize the sensor, read the values, de-energize the sensors, check if the soil needs watering, check if it’s time to send the spreadsheet update, etc”.

The most difficult part of most projects is not the mechanics of writing the code, it is thinking about the code, and making sure you have an organized plan. For one project I did while I was in school, we spent a few days planning how the radio transmitter and receiver would communicate with each other while also conserving power, after several discussions and revisions, the code was banged out in a couple hours and worked perfectly the first time through (after fixing some transcription errors).