Growing stuff and doing things

DopeDaniel

Taste The Spectrum
IPM Forum Moderator
A buddy of mine is building an arcade cabinet with rotating screen so it can play cames like centipede and defender. He's got the motor on limit switches now but has to use trim to get it level. He's thinking about using an accelerometer. Thought you might have an elegant solution.
 

dstroy0

Zeroes and Ones
A buddy of mine is building an arcade cabinet with rotating screen so it can play cames like centipede and defender. He's got the motor on limit switches now but has to use trim to get it level. He's thinking about using an accelerometer. Thought you might have an elegant solution.

I think the best solution for this would be an absolute position sensor.

 

dstroy0

Zeroes and Ones
A buddy of mine is building an arcade cabinet with rotating screen so it can play cames like centipede and defender. He's got the motor on limit switches now but has to use trim to get it level. He's thinking about using an accelerometer. Thought you might have an elegant solution.

Using an accelerometer is slightly more involved


edit: 100% an accelerometer is a solution to this problem too, just a little more software engineering involved
 

dstroy0

Zeroes and Ones
Either way, an absolution position sensor mechanically connected to the object in motion would be easier to implement in software than an accelerometer. But installing the accelerometer is a lot easier than installing a rotary encoder unless you were planning to install the encoder because then there's somewhere to pick off the motion.
 

dstroy0

Zeroes and Ones
There's also lidar, he could stick a target on the sides of the monitor that will present to the sensor. Might run into the leveling problem still though.

 

dstroy0

Zeroes and Ones
Lemon royale

looks like bumping the temps up to 84f brought the leaves up to 86f. I’m at 85% strength jacks 3.6a-2.4b-1.8g epsom. I’m not seeing any P problems, plant is a uniform green color. The root chamber is around 15-20f cooler than ambient, so having temps at 70f wasn’t doing me any favors for P uptake. I added more mg because I saw some lightening between veins on lower leaves.

F0384F3F-0D9E-4E15-9B55-B208C381D341.jpeg 036F3C8C-A338-4177-B1D2-CFC32727B436.jpeg 2D83D597-D608-4DE5-86FA-E5141DF73267.jpeg 95E1B3DA-6BA7-4064-B792-96F96A7D8BE5.jpeg F966D2F2-FEEE-4A34-8DD2-C79C86E58D0D.jpeg
 

dstroy0

Zeroes and Ones
This is what I'm using to calculate VPD, and get my leaf surface temperatures.
I'm only able to get like 14-15 bits of resolution out of the ads1115, and it seems like reducing samples per second on the gray market ads1115 itself in setup gets rid of some noise.

Code:
void setup(void)
{  
  ads1115_0x48.begin(0x48);  // Initialize ads1115 at address 0x48 3.3v max input
  ads1115_0x48.setGain(GAIN_ONE);
  ads1115_0x48.setDataRate(RATE_ADS1115_128SPS);
}

/*
  mathematical constants
*/
#define ENUMBER 2.7182818284590452353602874713527 //euler's number

/*
  thermistor settings
*/
#define THERMISTORNOMINAL 100000 // resistance at 25 degrees C

#define TEMPERATURENOMINAL 25 // temp. for nominal resistance (almost always 25 C)

#define NUM_SAMPLES 5 // how many samples to take and average

#define BCOEFFICIENT 3950 // The beta coefficient of the thermistor (usually 3000-4000)

/*
  0x48 ads1115
  series resistor values
  measure resistance before installing and record it here
*/
#define AIN0_R 100000
#define AIN1_R 100000
#define AIN2_R 100000
#define AIN3_R 100000

/*
  0x48 ads1115
  thermistor 1-4 temperature calibration offset in degrees Celsius
*/
#define THERMISTOR_0_TEMP_C_OFFSET 0
#define THERMISTOR_1_TEMP_C_OFFSET 0
#define THERMISTOR_2_TEMP_C_OFFSET 0
#define THERMISTOR_3_TEMP_C_OFFSET 0

void get_thermistor_temperatures()
{
  //thermistor voltage divider series resistors
  const uint32_t SERIES_RESISTOR_VALUES[4] = {AIN0_R,
                                              AIN1_R,
                                              AIN2_R,
                                              AIN3_R
                                             };
                                            
  //temperature offset in degrees Celsius
  const float offset[4] = {THERMISTOR_0_TEMP_C_OFFSET,
                           THERMISTOR_1_TEMP_C_OFFSET,
                           THERMISTOR_2_TEMP_C_OFFSET,
                           THERMISTOR_3_TEMP_C_OFFSET
                          };
                          
  static uint8_t num_ads_samples = 0; //sample index
  static uint16_t samples[4][NUM_SAMPLES] = {0}; //2d sample array
  static bool calculate_temp = false; //flag is assigned true when num_ads_samples is assigned 0 (rollover)
  float average[4] = {0}; //array to hold sample averages
  float steinhart[4] = {0}; //array to hold temperatures

 
  static uint32_t timer = millis(); //sample delay timer
  if ((millis() - timer) >= 5UL) //non blocking delay
  {
    if (num_ads_samples <= NUM_SAMPLES) //only take a sample if the samples[R][C] array C index value is less than the NUM_SAMPLES macro
    {
      for (uint8_t i = 0; i < (NUM_SAMPLES-1); i++) //count to NUM_SAMPLES - 1
      {
        samples[i][num_ads_samples] = ads1115_0x48.readADC_SingleEnded(i); //start a single ended conversion, if this is not set to blocking in setup this will block       
      }
      num_ads_samples++; //increment the index
      if (num_ads_samples > NUM_SAMPLES)  //if the index is greater than the number of samples we are supposed to take (last sample)
      {
        num_ads_samples = 0; //reset the index from 6 to 0
        calculate_temp = true; //we've got enough readings to average that we can calculate the temp
      }
    }
    timer = millis(); //reset the timer
  }

 
  if (calculate_temp == true) //if we have the samples
  {
    for (int i = 0; i < 4; i++) //outer for loop is where the temperature calculation happens
    {
      
      for (int j = 0; j < NUM_SAMPLES; j++) //inner for loop
      {
        average[i] += float(samples[i][j]); //use inner for loop to add up all the samples from one ads1115 channel
      }
      average[i] /= float(NUM_SAMPLES); //average the samples
      
      // convert the value to resistance
      float resistance = ((31500.0 / average[i]) - 1.0);
      resistance = float(SERIES_RESISTOR_VALUES[i]) / resistance;

      // convert to temperature
      // magic number 273.15 is to convert from kelvin to degrees Celsius
      steinhart[i] = resistance / float(THERMISTORNOMINAL);
      steinhart[i] = log(steinhart[i]);
      steinhart[i] /= float(BCOEFFICIENT);
      steinhart[i] += 1.0 / (float(TEMPERATURENOMINAL) + 273.15);
      steinhart[i] = 1.0 / steinhart[i];
      steinhart[i] = steinhart[i] - 273.15;
      tx_payload.leaf_temp_c[i] = steinhart[i] + offset[i]; // deg celsius + calibration offset
      //c to f (0°C × 9/5) + 32
      tx_payload.leaf_temp_f[i] = (tx_payload.leaf_temp_c[i] * (9 / 5)) + 32;
    }
    
    for (int i = 0; i < 4; i++)
    {
      for (int j = 0; j < NUM_SAMPLES; j++)
      {
        samples[i][j] = 0; //assign zero to samples[R][C] array elements
      }
    }
    VPDcalc(); //calculate vapor pressure deficit with new leaf temperatures
    calculate_temp = false; //we just calculated, time to take more samples
  }
}

void VPDcalc()
{
  /*
     Calculate VPD and convert to kPa

     SVP formula, e is euler's number T is temp in celsius
     SVP = 610.78 x e^(T / (T + 238.3) x 17.2694))
  */

  float SVP[5] = {0.0, 0.0, 0.0, 0.0, 0.0};
  float vpd_kpa[5] = {0.0, 0.0, 0.0, 0.0, 0.0};
  float rh;
  float temp[5] = {0.0, 0.0, 0.0, 0.0, 0.0};

  temp[0] = tx_payload.air_temperature_c;
  for (uint8_t i = 0; i < 4; i++)
  {
    temp[i + 1] = tx_payload.leaf_temp_c[i];
  }
  rh = tx_payload.air_relative_humidity;

  for (int i = 0; i < 5; i++)
  {
    float SVPexp = ((temp[i] / (temp[i] + 238.3)) * 17.2694);
    float SVPpow = pow(ENUMBER, SVPexp);
    float SVP_t = (610.78 * SVPpow) / 1000.00; //convert to kpa
    SVP[i] = SVP_t;
    if (i == 0)
    {
      //VPD = SVP * (1.00 - rh / 100.00)
      vpd_kpa[i] = ((1.00 - (rh / 100.00)) * SVP[i]);
    }
    else
    {
      //LVPD = LSVP - (SVP * (rh / 100));
      vpd_kpa[i] = (SVP[i] - (SVP[0] * rh / 100.00));
    }
  }
  for (int i = 0; i < 5; i++)
  {
    tx_payload.vpd_kpa[i] = vpd_kpa[i];
  }
}
 

dstroy0

Zeroes and Ones
ESP32 environmental sensor code

I still need to add the differential pressure sensor and TDS sensor functions but I think that's all.

Documentation wise I need to write up the initial calibration procedures but that wont take long.

edit: it's 6mb but most of that is from the RF24 and RF24Network libraries

RF24 doc

RF24Network doc
 

Attachments

  • _area_sensor_template.zip
    6.3 MB · Views: 0
Top Bottom