# Data Acquisition, Display and Stability Monitoring on the Hygromax 630

I had mentioned in an earlier blog that I was using a couple of 74HC4067s to expand my data acquisition system to 32 input lines and 4 MAX7219s to display this data with the help of 32 7-segment LED displays . In this blog, I'll elaborate on the programming that went into making this happen.
The 74HC4067 is a high-speed, 16-channel Analog Multiplexer with 4 control inputs to select any 1 of 16 channels and an Enable pin that can be used to choose (a seriously large amount of time could have been saved, in my case, had I only glanced at the datasheet and realized that the EN printed on the breakout board was, in fact, active LOW and had a bar drawn over the letters ) between the multiplexers.

While the inputs can be computed so that code is minimized, hard coding these values would be much faster to execute. Also, the inputs are fixed, so no substantial gain in keeping them configurable. The patterns to be shifted into the register to select channel 0 to 31 now become

Code:
unsigned char ADC_SELECT[32] = {32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,
16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31};
I don't really need the entire system to be monitored every second, so a simple round-robin scheduling of these scans takes the load off the ADC by requesting a subset of characteristics to be read (and displayed) every second.
Code:
    int selected_disp = mc_seconds % 6;
switch (selected_disp)
{
case 0:
//Light
ScanLight();
break;
case 1:
// Humidity Temperature
ScanHumidity();
break;
case 2:
// Oxygen
ScanOxygen();
break;
case 3:
//CO2
ScanCO2();
break;
case 4:
//pH
ScanpH();
break;
case 5:
//Water Level
ScanWaterLevel();
break;
}
(The DHT-11 has it's own digital protocol to receive temperature and humidity values that it senses. I'm using a library contributed by Eric Fossum to interact with these sensors. Also, the MH-Z14 CO2 sensor has both a digital readout as well as an analog interface. Since the accuracy isn't critical in this case, and also I'm saving up pins that I know I will need later for the Level and Stability Monitor, I opted for the analog mode).

The MAX7219 ensures that no effort is wasted by the ARM processor in maintaining the signals needed to keep the display alive and flicker-free. 8 7-segment displays per tray, and a set of 8 displays for the system clock would require an absurd number of GPIO pins for the 32 displays and make this project impossible to implement otherwise.

The MAX7219 operates in two stages - Initalization and Display. Any communication with this device requires two bytes of data - Register Address and Data to be written into that register. The DataOut pin on the 7219 acts as a Serial Out and makes the data at DataIn available at this pin, 8 clock cycles later. This allows us to cascade the 4 displays and a modification of the SPI write function (SPI_Write8) allows us to now write 8 bytes (2 per display) into all 4, as if they were a single display system. Since data from the sensors is read one by one (since a single ADC is being used), it would be quite wasteful to keep trying to update the display as and when data is available. A better approach would be to store the data and accumulate it till there is enough to display on all 4 display units.

Code:
//Write into the display buffer
void SPI_WriteToBuf(int bufslot, int digit, unsigned char MSB, unsigned char LSB)
{
BUFFER_MSB[bufslot][digit] = MSB;
BUFFER_LSB[bufslot][digit] = LSB;

}
and then call the appropriate function to send out the data to be displayed.

Code:
/// Send eight bytes on SPI bus to 4 7219's
void SPI_Write8BUF()
{
for(int digit=0;digit<8;digit++)
{

spi.write(BUFFER_MSB[3][digit]);                 // Send two bytes to 3
spi.write(BUFFER_LSB[3][digit]);
spi.write(BUFFER_MSB[2][digit]);                 // Send two bytes to 2
spi.write(BUFFER_LSB[2][digit]);
spi.write(BUFFER_MSB[1][digit]);                 // Send two bytes to 1
spi.write(BUFFER_LSB[1][digit]);
spi.write(BUFFER_MSB[0][digit]);                 // Send two bytes to 0
spi.write(BUFFER_LSB[0][digit]);

}
}
(A practical tip for anyone replicating this system - when you line up 8 7-segments and want an easy way to plug them in and out, use two 40-pin IC bases on your PCB, side by side. Switching out a defective display becomes effortless)

Like most SPI projects, it was impossible to get this one to work without spending hours staring at the Logic Analyzer traces. So many mistakes just waiting to happen when there are 4 displays on a single bus.

A small side-note on the Stability Monitor. The way I've designed it, the system waits for any disturbance and detects which side or corner has been compromised. I've used 4 dual color (RED-GREEN) LEDs to represent the corners. All GREEN would mean that everything is fine, RED on a single LED implying a corner was affected, RED on two LEDs implying the side connecting them was compromised. Once the disturbance has been detected, the system stays in that state, issuing an alert, till the RESET button (in the middle) is pressed, returning it to a stable (ALL GREEN) state. I've written the code to ensure what, I felt, was a minimum number of comparisons to arrive at an evaluation as quickly as possible (resulting in the code looking clumsy and obfuscated).

The basic logic behind obtaining the displacement (angular) from the gyroscope readings would be to integrate the angular velocity over time to arrive at the angular displacement. I am not worrying too much about the magnitude of displacement right now, so the accuracy isn't important. It is only important to ascertain whether or not there was an incident.

Code:
#define    GYRO_TRIGGER            5.0   // basically sets the sensitivity of the system
#define GYRO_SCAN_INTERVAL_MS    200         // smaller interval would mean a more accurate integral of angular velocity
[/CODE}

200ms should be small enough an interval to detect any disturbance to the unit and large enough to allow all other operations to be carried out comfortably.

[CODE]
while (1) {

imu.getGyroAccXYZandSensorTime(accData, gyroData, sensorTime, accConfig.range, gyroConfig.range);
imu.getTemperature(&imuTemperature);

if(!disturbed)
{

//
//     2     1
//
//     3     4

int xval = gyroData.xAxis.scaled;
int yval = gyroData.yAxis.scaled;
int zval = gyroData.zAxis.scaled;

disturbed = 1;

if(xval > GYRO_TRIGGER)  // 3 or 4 lowered
if(yval > GYRO_TRIGGER) // 4
else if(yval < -(GYRO_TRIGGER))  //3
else // 3 and 4
else if(xval < -(GYRO_TRIGGER))  // 1 or 2 lowered
if(yval > GYRO_TRIGGER) // 1
else if(yval < -(GYRO_TRIGGER))  //2
else
else if(yval > GYRO_TRIGGER) // 1 and 4
else if(yval < -(GYRO_TRIGGER)) // 2 and 3
// else // basically, all is well - no disturbance
}  // if(!disturbed)
wait_ms(GYRO_SCAN_INTERVAL_MS);

} // while(1)
}
Below is a picture of the board with the LEDs and the RESET button. I can't afford 8 output pins for the LEDs, so it's back to Ollie's ShiftOut library and my stash of 164's.

Author
avitron
Views
443