There are many types of chemicals and gasses that can be stored in a garage or shed. Some of these include gasoline, paint, propane tanks, car exhaust, paint thinner, natural gas from an leaky water heater, etc...
An air quality monitor can alert you of unhealthy or hazardous conditions, control an exhaust fan, and to log air quality history. The idea is to be alerted of marginal (unhealthy) conditions and correct the problem, or in the case of dangerous conditions to automatically turn on an exhaust fan and vent the hazardous gasses.
If monitoring local air quality was more common, as well as eliminating the sources, then perhaps even global environmental air quality could improve.
The Kinetis FRDM-K82FThe Kinetis MK82FN256VLL15 is a powerful chip that includes a Cortex-M4 core at 150 MHz, 256KB of SDRam, USB and advanced security.
The FRDM-K82F board includes 4MB of QuadSPI Flash memory, analog to digital converters, a low-powered UART, AES encryption, hashing, a FlexIO port that supports an OV7670 camera board, a real-time clock, an on-chip temperature sensor and GPIO.
This project will demonstrate these features along with voltage-level shifting, RS-232 communication with an Arduino and a printer, an on-screen display, and controlling an output device with a transistor.
OperationI am using an MQ-7 gas sensor to monitor air quality. FlexIO is used to receive video using an OV7670 camera board. Sensor data is overlayed into the video feed that includes:
- Air quality ADC digital counts with color coded for quality levels of Good (green), Unhealthy (yellow) and Hazardous (red).
- Date and Time provided by the Real-Time Clock
- Temperature provided by the on-chip temperature sensor
Air quality level changes are logged to serial NOR flash via QSPI, and are encrypted with AES.
A low power UART is used to control a printer and to receive RFID tag info. The Tag UID is hashed to compare with a stored value for authentication.
A GPIO port controls a fan based on the air quality.
I am also using an Arduino to process and simplify the RFID input.
Windows 10 WarningThe best advice at this time seems to be to not use Windows 10 with the FRDK-K82F board.
Windows 7 or 8 is preferred. The reason is that in some cases the bootloader may become corrupted on the K20 chip, which manages debugging and programming. When the bootloader is corrupted you cannot program the chip through the USB port, and the board is unusable.
If you have already upgraded all of your computers to Windows 10, like I have, then there is a workaround where you can update the OpenSDA firmware. This must be done immediately and preferably on a Windows 7 or 8 computer. For more information see Appendix C in Getting Started with Kinetis SDK (KSDK) v.1.3 and Kinetis FlexIO – All the Information you wanted to know.
However, in my case my USB drivers had not been updated for Windows 10 which caused them to randomly disconnect the FRDM-K82F board. So when I tried to update the OpenSDA firmware it got corrupted and my board was unusable. My solution was to replace the FRDM-K82F board.
Learning about the FRDM-K82FTo get familiar with the FRDM-K82F board I started with the NXP videos available on Hackster, the FAQs, and the Camera Demo Project.
Logic Level ConverterThe FRDM-K82F has a Low Power UART transceiver to communicate with devices using RS-232. In this case we are sending data to a printer and receiving data from an Arduino. The printer RS-232 interface needs a 5V signal to receive a clear message. The Arduino uses 5V but this could damage the 3.3V inputs of the FRDM-K82F board.
To shift the voltages we can use a level shifter board such as the SparkFun BOB-12009. There is a nice description of the board at SparkFun.
In this case we provide 5V to the high voltage side, 3.3V to the low voltage side and connect a common ground. Then connect the FRDM-K82F I/O to the low voltage side, and the 5V peripherals to the high voltage side.
In this section I will demonstrate the workflow that I used to add features to your code. In this case I am adding a GPIO (General Purpose Input/Output) output pin that will control a fan to my existing project which is based on the FlexIO Camera Demo project.
I started with a demo project that is similar to what I wanted to achieve. For this project I used the FlexIO camera demo in \SDK_2.0_FRDM-K82F\boards\frdmk82f\usb_examples\usb_device_video_flexio_ov7670 (your path may be different). You will need to order an OV7670 camera board and solder on the headers. After the camera was installed I got it working with YawCam. More info about the camera demo project can be found here.
To setup your development environment see the NXP Freedom Development Platform tutorial. The tutorial is pretty straightforward but be prepared to spend an hour or more setting it up.
I am using SDK 2.0 for this project (as opposed to SDK 1.3) because 2.0 supports the OV7670 camera board.
Reference MaterialsAs previously mentioned, the FRDM-K82F board is powerful and flexible. One thing to note is that along with FlexIO, each pin can have one of seven different configurations. For more information see the FRDM-K82F User's Guide.
You may want to keep the following references handy:
- The flyer that came with your FRDM-K82F board, which has a picture of the board and shows the ports and pins. This can help you find the Jumper and Pin numbers.
- The K82 Sub-Family Reference Manual. I printed out pages 216-222, where the pin configurations can be found because I use this frequently to find an available port.
- I also printed out the FRDM-K82F Schematics. This helps to show the on-board connections, and explains some of the functionality (like why is the RGB LED glowing when the camera is on? Answer: the same port is used as the camera's video sync signal.
A simple circuit can power a small fan from a 9V battery. This circuit uses a TIP120 transistor, a resistor and a diode to control a tiny 12V PC computer fan:
So I want to add a GPIO port to the Camera Demo Project.
A simple project in the SDK that shows basic GPIO can be found in the SDK at SDK_2.0_FRDM-K82F\boards\frdmk82f\driver_examples\gpio\led_output. This demo shows how to blink an LED using GPIO.
All of the basics of using GPIO are in this project, like configuring a GPIO port as an output, setting the pin configuration, and turning the port on and off. I can run this demo in Kinetis Design Studio by itself and tinker with it until I have the functionality that I need.
Check the PortsIf you are adding to an existing project, you should check to see if the port you need is already being used. To do this we can look in the FlexIO Camera Demo's pin_mux.c file and see which ports are in use. It turns out that quite a few ports are being used for the FlexIO port and we don't want to change those ports. But by checking the schematics and pin_mux.c file it looks like Port C, Pin 11 is unused. I can see that the GPIO functionality exists for Port C Pin 11 by also checking the K82 Sub-Family Reference Manual.
Here we see that under the heading Pin Name we can find PTC11. By default it is disabled. So we need to use the ALT1 configuration for it to be a General Purpose I/O port PTC11.
This works in the PORT_SetPinMux() method because kPORT_MuxAsGpio is defined as 1, for ALT1.
You can verify this in Kinetis Design Studio by hovering your cursor over kPORT_MuxAsGpio:
Now you will have to modify your pin_mux.c file to configure Port C Pin 11 to be a GPIO pin:
/* Fan pin mux Configuration */
PORT_SetPinMux(PORTC, 11U, kPORT_MuxAsGpio); //Fan
Configure the Port in CodeNow that the pin has been set to be a GPIO port you can write code to configure and initialize the port:
/* Define the init structure for the output (FAN) pin*/
//The zero indicates that the pin will be off by default
gpio_pin_config_t fan_config = {kGPIO_DigitalOutput, 0};
/* Init output GPIO pin to Port C Pin 11 */
GPIO_PinInit(GPIOC, 11, &fan_config);
And turn the port on:
GPIO_SetPinsOutput(GPIOC, 1U << FAN_GPIO_PIN); //Fan ON
or off:
GPIO_ClearPinsOutput(GPIOC, 1U << FAN_GPIO_PIN); //Fan OFF
A UART (Universal Asynchronous Receiver/Transmitter) is an electronic component that transfers serial information between computers. There is a low power UART (LPUART) in the MK82FN256VLL15 on the FRDM-K82F board that will be used to send data to a printer and receive data from an RFID card reader.
You must add definitions, configure and initialize the low power UART:
#define DEMO_LPUART LPUART0
#define DEMO_LPUART_CLKSRC kCLOCK_Osc0ErClk
uint8_t ch;
lpuart_config_t config;
uint8_t txbuff[] = "";
uint8_t rxbuff[20] = {0};
CLOCK_SetLpuartClock(2U);
LPUART_GetDefaultConfig(&config);
config.baudRate_Bps = 9600;
config.enableTx = true;
config.enableRx = true;
LPUART_Init(DEMO_LPUART, &config, CLOCK_GetFreq(DEMO_LPUART_CLKSRC));
Now you can write a character to the printer:
LPUART_WriteBlocking(DEMO_LPUART, output, sizeof(output) - 1);
Or read a character from the RFID reader:
LPUART_ReadBlocking(DEMO_LPUART, &ch, 1); cardId[1] = ch;
I am using a SUNFOUNDER RFID-RC522 board to read an RFID card UID (a unique, permanent ID). I could not use the RFID-RC522's serial interface, so I connected it to an Arduino through SPI. The problem that I had was that I could not find any libraries for the FRDM-K82F that supported the RFID reader directly, but it was easy to find one for the Arduino.
It turns out that the Arduino simplified the hardware and software interface to the FRDM-K82F board.
First I loaded the libraries and code into the Arduino. The Arduino code is based on the sketch rfid-master\rfid-master\examples\DumpInfo.ino which can be found on GitHub. I tested on the Arduino, and modified the code so that only the UID of the card was sent out through the Arduino's serial port.
Next I connected the Arduino serial output to the FRDM-K82F serial input port for the LPUART.
I used a small, inexpensive oscilloscope to check the signals (Sainsmart DSO-138). RS-232 can be a little tricky sometimes so it helps to see what you are doing.
I had to modify the code on the Arduino again to prevent the UID from constantly streaming while the card was in proximity of the RFID reader. I just added a 10s delay after the card was read. This is probably a little more secure anyway, and simplifies the code on the FRDM-K82F.
The Arduino reads the UID from the RFID card through SPI, and the FRDM-K82F board reads the UID from the Arduino through the LPUART, one character at a time:
//ReadBlocking means that this method will wait until a character is available
status_t status = LPUART_ReadBlocking(DEMO_LPUART, &ch, 1);
if (status == kStatus_Success) //Success means the RFID reader is sending
{
cardId[0] = ch; //Save the first character of the card's User Id
LPUART_ReadBlocking(DEMO_LPUART, &ch, 1); cardId[1] = ch; //Read and save the rest of the user id
LPUART_ReadBlocking(DEMO_LPUART, &ch, 1); cardId[2] = ch;
LPUART_ReadBlocking(DEMO_LPUART, &ch, 1); cardId[3] = ch;
LPUART_ReadBlocking(DEMO_LPUART, &ch, 1); cardId[4] = ch;
LPUART_ReadBlocking(DEMO_LPUART, &ch, 1); cardId[5] = ch;
LPUART_ReadBlocking(DEMO_LPUART, &ch, 1); cardId[6] = ch;
LPUART_ReadBlocking(DEMO_LPUART, &ch, 1); cardId[7] = ch;
cardId[8] = 0; //Add a null terminator
LPUART_ReadBlocking(DEMO_LPUART, &ch, 1); //Read and ignore the end-of-message characters
LPUART_ReadBlocking(DEMO_LPUART, &ch, 1);
...
}
PrinterWhen a valid RFID card is scanned the log file is printed out as shown below. If an invalid card is scanned (which does not match the stored hash code) the event is logged and nothing is printed out. When the air quality changes an event is also logged:
Here is the printer schematic. You may not need the voltage level shifter before the RS232 converter. The NS-RS232-02 should work from 3-5V:
I am using a thermal receipt printer for printing logs. The model I am using is a Star TSP600 with a serial interface card that receives RS-232 at 9600 baud N-8-1 (with no parity, 8 bits and 1 stop bit). It has 48 columns so I try to limit the messages to 48 characters. The printer would not work consistently on the 5V from the level converter so I used an NS-RS232-02 RS232 to TTL converter.
The command to print a line:
LPUART_WriteBlocking(DEMO_LPUART, output, sizeof(output) - 1);
The on-screen display shows the air quality in digital counts, and indicates quality by color, where green is good, yellow is unhealthy and red is hazardous.
The temperature of the MK82FN chip is shown next, and then the date and time from the real-time clock is shown in blue.
The current system status is displayed onscreen by rendering a custom 3x5 character set at the top of the screen. The characters are tiny due to the limited size of the display (160 x 120).
The screen origin (0,0) is at the bottom left.
Each character's data fits into 20 bits, with every 4 bits being a row of pixels. There is one leading column of pixels that are always 0, which gives the 3x5 character set actual dimensions of 4x5, making 20 bits per character.
For example, the character A would be 0x25755:
This way the 20 bit character data can be stored in a uint32_t, like this:
uint32_t characterBitmaps[] =
{ 0x00000, //(space)
0x22202, //!
0x55000, //"
0x57575, //#
0x36236, //$
0x51245, //%
0x22762, //&
0x22000, //'
0x12221, //(
0x42224, //)
0x05250, //*
0x02720, //+
...
For convenience, the character set is aligned with the ASCII character set. For example, the ASCII code for the letter A is a constant offset plus the index of the character data. This way an ordinary string can be passed in for display along with the desired color.
So starting at the highest level method, a string can be formatted to show the current time from the real-time clock. The drawString() method is called to convert the string ASCII data to character bitmap data.
RTC_GetDatetime(RTC, &date);
sprintf(outString, "%04hd-%02hd-%02hd %02hd:%02hd:%02hd ", date.year, date.month, date.day, date.hour, date.minute, date.second);
drawString(80,115, outString, 20, 0xF8, 0x00, buffer);
The outString parameter is decomposed into characters and passed into the drawChar() method:
void drawString(int x, int y, char s[], int length, uint8_t c1, uint8_t c2, volatile uint8_t *buffer)
{
for (int stringIndex = 0; stringIndex < length; stringIndex++)
{
//Draws a character on the screen at position x,y with color c1:c2
drawChar(x, y, c1, c2, s[stringIndex], buffer);
x += 4;
}
}
The drawChar() method sets the pixels in the video buffer at the desired position (x,y) and color (c1:c2):
void drawChar(int x, int y, uint8_t c1, uint8_t c2, char ch, volatile uint8_t *buffer)
{
const long firstMaskBit = 524288; //First bit in the bitmap
const int asciiOffset = 32; //Offset between the actual ASCII code and the bitmap index
int index = ch - asciiOffset;
long characterBitmap = characterBitmaps[index]; //Get the character bitmap
long maskBit = firstMaskBit;
for (int dy = 4; dy >= 0; dy--) //5 rows per bitmap (3 bits plus a leading 0)
{
for (int dx = 0; dx < 4; dx++) //3 columns per bitmap
{
if ((maskBit & characterBitmap) != 0)
drawPix(x + dx, y+dy, c1, c2, buffer); //Draw a pixel with color c1:c2
else
drawPix(x + dx, y + dy, 0, 0, buffer); //Draw a black pixel
maskBit = maskBit >> 1;//Shift the mask to the next pixel
}
}
}
The final method sets the desired pixel and color:
void drawPix(int x, int y, uint8_t c1, uint8_t c2, volatile uint8_t *buffer)
{
int address = x*2 + y*320;
buffer[address]=c1;
buffer[address+1]=c2;
}
Flash MemoryOne thing to note about working with the Flash memory is that it must be erased before it can be written to, otherwise you may read back garbage.
This is because when the Flash memory is erased it is set to all binary 1s, and when it is written to the code just sets the 1s to 0s that it needs to be 0s. So if you write to the same memory twice with different data, the information will be munged.
This code is based on the SDK 2.0 demo project in \SDK_2.0_FRDM-K82F\boards\frdmk82f\driver_examples\qspi\polling_transfer.
Here is the initialization code for the flash to show what is necessary. Just remember to erase before you program the flash or your data will be scrambled:
uint32_t flashClockSourceFreq = 0;
qspi_config_t qspi_config = {0};
QSPI_GetDefaultQspiConfig(&qspi_config);
/*Set AHB buffer size for reading data through AHB bus */
qspi_config.AHBbufferSize[3] = FLASH_PAGE_SIZE;
flashClockSourceFreq = CLOCK_GetFreq(QSPI_CLOCK_SOURCE);
QSPI_Init(EXAMPLE_QSPI, &qspi_config, flashClockSourceFreq);
QSPI_SetFlashConfig(EXAMPLE_QSPI, &single_config);
enable_quad_mode();
erase_sector(FSL_FEATURE_QSPI_AMBA_BASE); //Important!!
To log to the flash memory we can now use the following code which will get the current time from the real-time clock, format the time and a message as a string, and call the LogToFlash method. The numberOfLinesInLog counts the pages that have been written to the flash memory, with one message per page. Each page is 256 bytes:
RTC_GetDatetime(RTC, &date);
sprintf(flashMessageBuffer, "%04hd-%02hd-%02hd %02hd:%02hd:%02hd %s\r\n", date.year, date.month, date.day, date.hour, date.minute, date.second, " System Startup");
LogToFlash(numberOfLinesInLog, flashMessageBuffer);
Inside the LogToFlash method, the method program_page actually programs the flash, and the while loop waits until the status is not busy. So it is pretty easy to write to the flash memory. Incrementing the numberOfLinesInLog is used to point to the next page of flash memory for the next program_page operation:
void LogToFlash(int numberOfLines, unsigned char * message)
{
/* Call AES_cbc encryption */
g_length = 48;
for (int i=0;i<256;i++) cipher[i] = 0;
status_t status = LTC_AES_EncryptCbc(ltcbase, message, cipher, g_length, ive, key128, AES128_KEY_SIZE); //printString must be 16 byte multiple and 16 or more
if (status != kStatus_Success)
{
PRINTF("AES CBC encryption failed !\r\n");
}
enable_quad_mode();
//copy message to the buffer
for (int i = 0; i < 64; i++)
{
flashBuffer[i] = cipher[i];
}
program_page(FSL_FEATURE_QSPI_AMBA_BASE + numberOfLines*256, (uint32_t *)flashBuffer);
while (QSPI_GetStatusFlags(EXAMPLE_QSPI) & kQSPI_Busy)
{
}
numberOfLinesInLog ++;
}
Reading from flash is like reading from memory:
void ReadFlashLog(void)
{
PRINTF("Reading from Flash ...\r\n");
for (int messageCounter = 0; messageCounter < numberOfLinesInLog; messageCounter ++)
{
for (int i = 0; i < 256; i++)
{
cipher[i] = ((uint32_t *)(FSL_FEATURE_QSPI_AMBA_BASE + messageCounter * 256))[i];
}
for (int i=0;i<256;i++) output[i] = ' ';
/* Call AES_cbc decryption */
status_t status = LTC_AES_DecryptCbc(ltcbase, cipher, output, g_length, ive, key128, AES128_KEY_SIZE, kLTC_EncryptKey);
if (status != kStatus_Success)
{
PRINTF("AES CBC decryption failed !\r\n");
}
PRINTF("%s",output);
LPUART_WriteBlocking(DEMO_LPUART, output, sizeof(output) - 1);
}
}
The datasheet for the flash memory can be found here.
Real-Time ClockThe real-time clock code is based on the rtc project which is in the SDK 2.0 Framework folder in \SDK_2.0_FRDM-K82F\boards\frdmk82f\driver_examples\rtc.
The steps involved are simple:
- Get the default configuration
- Enable the 32KHz oscillator
- Set a start date and time
- Read and format the date and time
RTC_GetDefaultConfig(&rtcConfig);
RTC_Init(RTC, &rtcConfig);
/* Enable the RTC 32KHz oscillator */
RTC->CR |= RTC_CR_OSCE_MASK;
/* Set a start date time and start RT */
date.year = 2016U;
date.month = 6U;
date.day = 14U;
date.hour = 11U;
date.minute = 17;
date.second = 0;
/* RTC time counter has to be stopped before setting the date & time in the TSR register */
RTC_StopTimer(RTC);
/* Set RTC time to default */
RTC_SetDatetime(RTC, &date);
/* Start the RTC time counter */
RTC_StartTimer(RTC);
Here is an example of reading and formatting the date and time:
RTC_GetDatetime(RTC, &date);
sprintf(outString, "%04hd-%02hd-%02hd %02hd:%02hd:%02hd ", date.year, date.month, date.day, date.hour, date.minute, date.second);
Analog to Digital ConverterThe Analog to Digital converter code is based on the adc16 polling project which is in the SDK 2.0 Framework folder in \SDK_2.0_FRDM-K82F\boards\frdmk82f\driver_examples\adc16\polling.
An internal temperature sensor is available on channel 26, and the Air Quality channel for this project is channel 1.
Channel 26 is internally connected to the temperature sensor.
Channel 1 is routed to pin 5 of jumper 3, which is the analog input signal from the air quality sensor:
#define DEMO_ADC16_BASE ADC0
#define DEMO_ADC16_CHANNEL_GROUP 0U
#define ADC16_TEMP_CHANNEL 26U //Temp Sensor
#define ADC16_AIR_QUALITY_CHANNEL 1U //Air Quality Sensor
After an optional calibration, you can set the channel configuration to the appropriate structure for temperature or air quality and read the digital equivalent of the voltage input:
//Globals
int sampleAirQuality=0, sampleTemperature=0;
int emaAirQuality = 0, emaTemperature = 0;
...
Config and read air quality
adc16ChannelConfigStruct.channelNumber = ADC16_AIR_QUALITY_CHANNEL;
ADC16_SetChannelConfig(DEMO_ADC16_BASE, DEMO_ADC16_CHANNEL_GROUP, &adc16ChannelConfigStruct);
while (0U == (kADC16_ChannelConversionDoneFlag & ADC16_GetChannelStatusFlags(DEMO_ADC16_BASE, DEMO_ADC16_CHANNEL_GROUP))) {}
sampleAirQuality = ADC16_GetChannelConversionValue(DEMO_ADC16_BASE, DEMO_ADC16_CHANNEL_GROUP);
Config and read chip temperature
adc16ChannelConfigStruct.channelNumber = ADC16_TEMP_CHANNEL;
ADC16_SetChannelConfig(DEMO_ADC16_BASE, DEMO_ADC16_CHANNEL_GROUP, &adc16ChannelConfigStruct);
while (0U == (kADC16_ChannelConversionDoneFlag & ADC16_GetChannelStatusFlags(DEMO_ADC16_BASE, DEMO_ADC16_CHANNEL_GROUP))) {}
sampleTemperature = ADC16_GetChannelConversionValue(DEMO_ADC16_BASE, DEMO_ADC16_CHANNEL_GROUP);
The while loops wait for the ADC conversion to finish.
Now you can use the digital output of the sensors. Note that the digital values will not mean much until calibrated. Also, because of noise, the values will tend to be jumpy and will need to be smoothed for display, or when used as boundary values:
To smooth the input values we could take the average of the past several inputs. For example we could average the last 10 inputs to get an average value and the result will be a smoother transition in sequential readings. However we would need to keep track of 9 past values and average them every time.
If we don't require an evenly weighted average value we can use a smoothing function, such as an exponential moving average. The values are weighted but the calculation is simple, and we only need to keep track of one value.
The formula for the temperature looks like this:
EMATemperature = currentTemperature * 1/32 + EMATemperature * 31/ 32.
The M4 chip is very fast and we don't really have to worry about division, but as an example, we can use powers of two to shift and save dividing. For example:
const int TempAlpha = 5;//alpha (smoothing constant) = 1/32
const int AirQAlpha = 7;//alpha (smoothing constant) = 1/128
if (emaAirQuality == 0) emaAirQuality = sampleAirQuality; //initialize
emaAirQuality = (sampleAirQuality + (emaAirQuality << AirQAlpha) - emaAirQuality) >> AirQAlpha;//smooth
if (emaTemperature == 0) emaTemperature = sampleTemperature; //initialize
emaTemperature = (sampleTemperature + (emaTemperature << TempAlpha) - emaTemperature) >> TempAlpha;//smooth
For more information see this Wikipedia Article on EMA.
Chip TemperatureFrom the application note we know that the temperature sensor output is linear and dependant on VDD, and that the temperature sensor output voltage is highest at cold temperatures and lowest at hot temperatures.
Knowing this, we can take two points and roughly calibrate the temperature to the ADC digital output.
The digital output of the temperature sensor was monitored until it stabilized at a count of about 862. Using an infrared thermometer the chip temperature was approximately 93 degrees Fahrenheit.
Cooling the chip to 77 degrees Fahrenheit with a can of compressed gas (for dusting) resulted in a digital reading of 895. With these two points the temperature can be linearly calibrated.
Knowing the points (862,93) and (895,71) we can solve for y=mx+b, simplify and optimize:
y=-2x/3 + 2003/3
y=(2003-2x)/3
y=(2003-2x)*341 >> 10 (optimization for division by three, although not really necessary)
And now we can read the temperature like this:
//Two point linear calibration: (862,93F) (895, 71F) => y=-2x/3 + 2003/3 => (2003-2x)/3 => (2003-2x) * 341 >> 10
int emaTempFahrenheit = (2003-2*emaTemperature) * 341 >> 10;
sprintf(outString, "%s%d%s", "TEMP:", emaTempFahrenheit,"F ");
More information on the on-chip temperature sensor can be found in this application note.
Air QualityTo measure the general air quality, I am using the MQ-7 sensor. It is sensitive to gases like Alcohol (hand sanitizer), Butane (a lighter) and Difluoroethane (compressed "air" duster), among other gasses. I am not calibrating the sensor since this is a demo project and I would not want to use any dangerous gasses to even calibrate with.
You can use the following circuit to wire up the MQ-7 to the ADC Input port:
Hooking up the sensor is pretty easy, and all you need is a resistor and some soldering.
See this article on the MQ135 for more information.
For testing I just use a drop of hand sanitizer on my finger and hold it up to the sensor.
When you first start the sensor it will have to warm up. I usually takes a few minutes to settle down to a value just below 400 digital counts for the sensor that I have. So for demonstration purposes I have chosen the ranges as follows:
Digital counts < 600 = Good
Digital counts 600 to < 800 Unhealthy
Digital counts > 800 Hazardous
if (emaAirQuality < 600)
airQualityStatus = Good;
else if (emaAirQuality < 800)
airQualityStatus = Unhealthy;
else
airQualityStatus = Hazardous;
CryptographyThanks to NXP/Freescale for making cryptography so easy to use. An example project can be found in \SDK_2.0_FRDM-K82F\boards\frdmk82f\driver_examples\ltc\aes.
Hash CodeOnce initialized, the cryptography functions are easy to implement:
LTC_Init(ltcbase);
For example, to compare the RFID card UID to a hashed value a single call is needed to compute the hash value. In this case the comparison uses the memcmp command. If the card is invalid, a message is logged to flash memory. If the card is valid (matches the hash code), the flash memory log is read and printed in the ReadFlashLog method:
LTC_HASH(ltcbase, kLTC_XcbcMac, cardId, 8, hashKey, 16, hash2, &hash_size);
if (memcmp(expectedPiccHash, hash2, hash_size))
{
PRINTF("Invalid card. Attempt has been logged.\r\n");
RTC_GetDatetime(RTC, &date);
sprintf(flashMessageBuffer, "%04hd-%02hd-%02hd %02hd:%02hd:%02hd %s\r\n", date.year, date.month, date.day, date.hour, date.minute, date.second, "Invalid card");
LogToFlash(numberOfLinesInLog, flashMessageBuffer);
}
else
{
PRINTF("Valid card!\r\n");
RTC_GetDatetime(RTC, &date);
sprintf(printString, "%04hd-%02hd-%02hd %02hd:%02hd:%02hd %s\r\n", date.year, date.month, date.day, date.hour, date.minute, date.second, cardId);
ReadFlashLog();
}
EncryptionI am using also using AES CBC Encryption to store the log files in Flash memory.
Encrypting a message to a cipher:
/* Call AES_cbc encryption */
g_length = 48;
for (int i=0;i<256;i++) cipher[i] = 0;
status_t status = LTC_AES_EncryptCbc(ltcbase, message, cipher, g_length, ive, key128, AES128_KEY_SIZE); //printString must be 16 byte multiple and 16 or more
if (status != kStatus_Success)
{
PRINTF("AES CBC encryption failed !\r\n");
}
Decrypting a cipher to output:
for (int i=0;i<256;i++) output[i] = ' ';
/* Call AES_cbc decryption */
status_t status = LTC_AES_DecryptCbc(ltcbase, cipher, output, g_length, ive, key128, AES128_KEY_SIZE, kLTC_EncryptKey);
if (status != kStatus_Success)
{
PRINTF("AES CBC decryption failed !\r\n");
}
Comments