J
Jan Panteltje
Guest
Testing the MQ4 gas sensor
So I bought a MQ4 gas sensor module:
https://www.tinytronics.nl/shop/en/sensors/air/gas/mq-4-gas-sensor-module
Had it on for 24 hours on the lab supply, in that period output on analog out dropped slowly to a very low value (takes hours).
Tested it in the kitchen, powered from a LM317 programmed for 5 V, thing uses about 170 mA,
meter on the analog output,
gas tap open, and low and behold the analog output went up considerable in a minute or so.
And went back to normal with no gas.
Put it on a piece of cloth drenched in some alcohol and that make the analog output rise too,
so thing is sensitive to alcohol too, may give false alarm in kitchen?
Anyways, whatever, I once a long time ago (2016). designed a wind sensor with a Microchip PIC 18F14K22 chip, it uses power over Ethernet,
and basically has 4 analog input channels, so borrowed one and connected it to the MQ4 module analog output via a resistor divider
(ADCc runs on 3.3V, the MQ4 on 5V
You can download the PIC asm, hexfile, and ciccuit here:
http://www.panteltje.nl/panteltje/pic/wind_pic_thermal_udp/wind_thermal-0.1.tgz
This wind_thermal is a nice way to get analog values into your computers via POE.
It sends UDP packets continuously to an IP address on the LAN that you can specify.
This is where the software comes in., to process it!
So I have one Raspberry Pi (a very old one) that runs as server for many things, much data in the house goes to that,
and can then be accessed from a website or via ssh etc, or by other computers on the LAN, that ls is what happens here now.
So for listening for UDP packets, looking at the received analog level from the MQ4 sensor, setting an alarm limit, I wrote the following code
The wind_thermal unit sends to IP address 192.168./178.73 (the raspi server) port 1078.
This is the C source code, it uses the \'netcat\' utility to receive and send data, netcat is one of the greatest network utilities I know about.
/* for internet */
#define _BSD_SOURCE /* this is needed to avoid compile error messages in internet routines */
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <wchar.h> // for open_wmemstream()
/* end for internet */
#include <stdio.h>
#include <string.h>
#include <termios.h>
#include <stdlib.h>
#include <sys/file.h>
#include <errno.h>
#include \"getopt.h\"
#include <math.h>
#include <unistd.h>
#include <ctype.h>
#include <time.h>
#include <stddef.h>
#include <pwd.h>
#include <math.h>
#include <pthread.h>
#include <stdint.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/time.h>
#include <stdio.h>
#include <stdlib.h>
int verbose;
int gas_alarm_level;
#define TEMP_SIZE 65535
void print_usage()
{
fprintf(stderr,\\
\"\\nPanteltje gas_pic level detector\\n\\
Usage:\\ngas_pic -h -l int -v\\n\\
\\n\\
-h help (this help).\\n\\
-l alarm_level, range 0 to 1000 default %d\\n\\
-v verbose, prints functions and arguments.\\n\"
, gas_alarm_level);
fprintf(stderr,\\
\"Examples, \\n\\
gas_pic -l 30\\n\\n\");
} /* end function print_usage */
int main(int argc, char **argv)
{
int a, b, c;
FILE *pptr;
int gas_adc_level;
char temp[TEMP_SIZE];
char out_string[TEMP_SIZE];
/* proces any command line arguments */
while(1)
{
a = getopt(argc, argv, \"hl:v\");
if(a == -1) break;
switch(a)
{
case \'h\': // help
print_usage();
exit(1);
break;
case \'l\': // gas alarm level
gas_alarm_level = atoi(optarg);
break;
case \'v\': // verbose
verbose = 1;
break;
default:
print_usage();
exit(1);
break;
} /* end switch a */
} /* end while command line argumants */
gas_adc_level = 0;
a = 0;
while(1)
{
sprintf(temp, \"netcat -u -l -p 1078\");
if(verbose)
{
fprintf(stderr, \"temp=%s\\n\", temp);
}
pptr = popen(temp, \"r\");
while(1)
{
c = fgetc(pptr);
if(verbose)
{
fprintf(stderr, \"%c\", c);
}
out_string[a] = c;
a++;
if(c == 10)
{
pclose(pptr);
a = 0;
if(verbose)
{
fprintf(stderr, \"out_string=%s\\n\", out_string);
}
// F23 R753 B747 L751a
sscanf(out_string ,\"F%d\", &gas_adc_level);
// if(verbose)
{
fprintf(stderr, \"gas_adc_level=%d\\n\", gas_adc_level);
}
if(gas_adc_level >= gas_alarm_level)
{
fprintf(stderr, \"gas alarm!!! gas_adc_level=%d gas_alarm_level=%d \\n\", gas_adc_level, gas_alarm_level);
sprintf(temp, \"echo \\\"alarm gas %d %d\\\" | netcat -q 1 -u 192.168.178.95 1078\", gas_adc_level, gas_alarm_level);
}
else
{
fprintf(stderr, \"gas_adc_level=%d gas_alarm_level=%d \\n\", gas_adc_level, gas_alarm_level);
sprintf(temp, \"echo \\\"gas %d %d\\\" | netcat -q 1 -u 192.168.178.95 1078\", gas_adc_level, gas_alarm_level);
}
if(verbose)
{
fprintf(stderr, \"executing %s\\n\", temp);
}
system(temp);
break;
} /* end if c == 10) */
} /* end while getc */
usleep(10000000);
} /* end while */
exit(1);
} /* end function main */
You can compile that code with
gcc -o gas_pic_udp gas_pic_udp.c
And then run it, with ADC alarm level (gas level) set to 100, like this:
gas_pic_udp -l 100 -v
-v for verbose shows you what is happening if run in a terminal
It now forwards the data to an other Raspberry on the LAN, 192.168.78.95 port 1078, alos in UDP packets
That is the main security computer, it record security cameras, audio, air traffic, ship traffic, temperature, humidity, CO level, many more things and now also gas level!
It has an about 4TB hard disc connected to it.. Also plays background music over a speaker if enabled (is always enabled, and here comes the fun
In the next code, on that rapi ..95 I run the following program to listen for the messages from that rapsi ..73.
#include <stdio.h>
#include <string.h>
#include <termios.h>
#include <stdlib.h>
#include <sys/file.h>
#include <errno.h>
#include \"getopt.h\"
#include <math.h>
#include <unistd.h>
#include <ctype.h>
#include <time.h>
#include <stddef.h>
#include <pwd.h>
#include <math.h>
#include <pthread.h>
#include <stdint.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/time.h>
int verbose;
int gas_alarm_level;
#define TEMP_SIZE 65535
pthread_t timer_thread;
pthread_attr_t *attributes;
int timer_running_flag;
int stop_timer_flag;
struct timeval timeout;
time_t now;
time_t ptime = 0;
struct tm *utc_time;
time_t old_ptime = 0;
int elapsed_seconds;
void *timer_routine()
{
time_t now;
if(verbose)
{
fprintf(stderr, \"timer_routine(): arg none\\n\");
}
timer_running_flag = 1;
while(1)
{
if(stop_timer_flag)
{
break;
}
usleep(1000000);
elapsed_seconds++;
if(verbose)
{
fprintf(stderr, \"elapsed_seconds=%d\\n\", elapsed_seconds);
}
if(elapsed_seconds > 30)
{
fprintf(stderr, \"ALARM no signal from gas sensor\\n\");
system(\"amixer sset PCM 100% 1>/dev/zero 2>/dev/zero\");
system(\"mpg123 /root/alarm_geen_signaal_van_gas_detector.mp3 1>/dev/zero 2>/dev/zero\");
usleep(3000000);
system(\"mpg123 /root/alarm_no_signal_from_gas_sensor.mp3 1>/dev/zeroo 2>/dev/zero\");
/* forc alarm voice repeat after 10 seconds */
elapsed_seconds = 20;
} /* end if elapsed_seconds > 30 */
} /* end while */
timer_running_flag = 0;
now = time(0);
fprintf(stderr, \"timer_routine(): exit at %s\\n\", ctime(&now) );
//pthread_exit(0);
return 0;
} /* end function timer_routine */
int start_timer_thread()
{
pthread_create(&timer_thread, 0, &timer_routine, 0);
if(verbose)
{
fprintf(stderr, \"start_timer_thread(): timer_thread_started\\n\");
}
return 1;
} /* end function start_timer_thread */
int main(int argc, char **argv)
{
int a, b, c;
FILE *pptr;
int gas_adc_level;
char temp[TEMP_SIZE];
char rx_string[TEMP_SIZE];
char * ptr;
/* proces any command line arguments */
while(1)
{
a = getopt(argc, argv, \"hl:v\");
if(a == -1) break;
switch(a)
{
case \'h\': // help
print_usage();
exit(1);
break;
case \'v\': // verbose
verbose = 1;
break;
default:
print_usage();
exit(1);
break;
} /* end switch a */
} /* end while command line argumants */
elapsed_seconds = 0;
start_timer_thread();
gas_adc_level = 0;
a = 0;
sprintf(temp, \"netcat -k -u -l -p 1078\");
if(verbose)
{
fprintf(stderr, \"executing=%s\\n\", temp);
}
pptr = popen(temp, \"r\");
// alarm gas 24 1
while(1)
{
c = fgetc(pptr);
if(verbose)
{
fprintf(stderr, \"%c\", c);
}
rx_string[a] = c;
a++;
if(c == 10) // !!! no close, keep reading line by line from netcat
{
a = 0;
if(verbose)
{
fprintf(stderr, \"rx_string=%s\\n\", rx_string);
}
// alarm gas 24 1
ptr = strstr(rx_string, \"alarm\");
if(ptr)
{
fprintf(stderr ,\"ALARM GAS\\n\");
system(\"amixer sset PCM 100%\");
system(\"mpg123 /root/gas_alarm_nl.mp3 1>/dev/zero 2>/dev/zero\");
}
/* reset timer */
elapsed_seconds = 0;
} /* end if c == 10) */
} /* end while getc */
exit(1);
} /* end function main */
So, you can compile that with
gcc -o gas_pic_udp_rx -lpthread gas_pic_udp_rx.c
So what does it DO?
It listens to the MQ4 messages it receives on UDP port 1078, and if it detects the word \'alarm\'
it set the audio mixer volume to maximum using amixer, and then plays audio file /root/gas_alarm_nl.mp3
It is a 50 W amp, and a big speaker, you will notice, all runs on UPS so...
But there was a catch, what if it received no messages from the first Raspberry for whatever reason?
So I added a timer thread, and if it times out on messages received it sets volume again to maximum and plays
/root/alarm_no_signal_from_gas_sensor.mp3
Nice female voices, made with this script using google:
gst5_en
#!/bin/bash
read user_reply
wget -O \"$user_reply\".mp3 \"http://translate.google.com/translate_tts?ie=UTF-8&client=tw-ob&q=$user_reply&tl=en\"
exit 0
you type
gst5_en
Then the text you want spoken foloed by ENTER key
and out comes the mp3 file with that text spoken.
If you wanted Dutch use =nl at the end, =de for German.
So, this code and hardware is released under the GPL licence.
I found an ethernet module as in the wind_thermal thing and a LM2596 switcher chip today in one of the many boxes with electronics here,
so will make a peeseebee and switcher to replace the LM317, plasic box to fit it all in Ifound too too.. PICs I have..
Busy! my new keyboard arrived, so far sound is very good...
https://www.bax-shop.nl/keyboards/fazley-fkb-180-61-toetsen-keyboard-zwart#productreviews
Very cheap, about 120 Euro / dollars.. sound alone is even better than more expensive speakers ...
Different keyboard to hit than this!!!
learning curve...
So I bought a MQ4 gas sensor module:
https://www.tinytronics.nl/shop/en/sensors/air/gas/mq-4-gas-sensor-module
Had it on for 24 hours on the lab supply, in that period output on analog out dropped slowly to a very low value (takes hours).
Tested it in the kitchen, powered from a LM317 programmed for 5 V, thing uses about 170 mA,
meter on the analog output,
gas tap open, and low and behold the analog output went up considerable in a minute or so.
And went back to normal with no gas.
Put it on a piece of cloth drenched in some alcohol and that make the analog output rise too,
so thing is sensitive to alcohol too, may give false alarm in kitchen?
Anyways, whatever, I once a long time ago (2016). designed a wind sensor with a Microchip PIC 18F14K22 chip, it uses power over Ethernet,
and basically has 4 analog input channels, so borrowed one and connected it to the MQ4 module analog output via a resistor divider
(ADCc runs on 3.3V, the MQ4 on 5V
You can download the PIC asm, hexfile, and ciccuit here:
http://www.panteltje.nl/panteltje/pic/wind_pic_thermal_udp/wind_thermal-0.1.tgz
This wind_thermal is a nice way to get analog values into your computers via POE.
It sends UDP packets continuously to an IP address on the LAN that you can specify.
This is where the software comes in., to process it!
So I have one Raspberry Pi (a very old one) that runs as server for many things, much data in the house goes to that,
and can then be accessed from a website or via ssh etc, or by other computers on the LAN, that ls is what happens here now.
So for listening for UDP packets, looking at the received analog level from the MQ4 sensor, setting an alarm limit, I wrote the following code
The wind_thermal unit sends to IP address 192.168./178.73 (the raspi server) port 1078.
This is the C source code, it uses the \'netcat\' utility to receive and send data, netcat is one of the greatest network utilities I know about.
/* for internet */
#define _BSD_SOURCE /* this is needed to avoid compile error messages in internet routines */
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <wchar.h> // for open_wmemstream()
/* end for internet */
#include <stdio.h>
#include <string.h>
#include <termios.h>
#include <stdlib.h>
#include <sys/file.h>
#include <errno.h>
#include \"getopt.h\"
#include <math.h>
#include <unistd.h>
#include <ctype.h>
#include <time.h>
#include <stddef.h>
#include <pwd.h>
#include <math.h>
#include <pthread.h>
#include <stdint.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/time.h>
#include <stdio.h>
#include <stdlib.h>
int verbose;
int gas_alarm_level;
#define TEMP_SIZE 65535
void print_usage()
{
fprintf(stderr,\\
\"\\nPanteltje gas_pic level detector\\n\\
Usage:\\ngas_pic -h -l int -v\\n\\
\\n\\
-h help (this help).\\n\\
-l alarm_level, range 0 to 1000 default %d\\n\\
-v verbose, prints functions and arguments.\\n\"
, gas_alarm_level);
fprintf(stderr,\\
\"Examples, \\n\\
gas_pic -l 30\\n\\n\");
} /* end function print_usage */
int main(int argc, char **argv)
{
int a, b, c;
FILE *pptr;
int gas_adc_level;
char temp[TEMP_SIZE];
char out_string[TEMP_SIZE];
/* proces any command line arguments */
while(1)
{
a = getopt(argc, argv, \"hl:v\");
if(a == -1) break;
switch(a)
{
case \'h\': // help
print_usage();
exit(1);
break;
case \'l\': // gas alarm level
gas_alarm_level = atoi(optarg);
break;
case \'v\': // verbose
verbose = 1;
break;
default:
print_usage();
exit(1);
break;
} /* end switch a */
} /* end while command line argumants */
gas_adc_level = 0;
a = 0;
while(1)
{
sprintf(temp, \"netcat -u -l -p 1078\");
if(verbose)
{
fprintf(stderr, \"temp=%s\\n\", temp);
}
pptr = popen(temp, \"r\");
while(1)
{
c = fgetc(pptr);
if(verbose)
{
fprintf(stderr, \"%c\", c);
}
out_string[a] = c;
a++;
if(c == 10)
{
pclose(pptr);
a = 0;
if(verbose)
{
fprintf(stderr, \"out_string=%s\\n\", out_string);
}
// F23 R753 B747 L751a
sscanf(out_string ,\"F%d\", &gas_adc_level);
// if(verbose)
{
fprintf(stderr, \"gas_adc_level=%d\\n\", gas_adc_level);
}
if(gas_adc_level >= gas_alarm_level)
{
fprintf(stderr, \"gas alarm!!! gas_adc_level=%d gas_alarm_level=%d \\n\", gas_adc_level, gas_alarm_level);
sprintf(temp, \"echo \\\"alarm gas %d %d\\\" | netcat -q 1 -u 192.168.178.95 1078\", gas_adc_level, gas_alarm_level);
}
else
{
fprintf(stderr, \"gas_adc_level=%d gas_alarm_level=%d \\n\", gas_adc_level, gas_alarm_level);
sprintf(temp, \"echo \\\"gas %d %d\\\" | netcat -q 1 -u 192.168.178.95 1078\", gas_adc_level, gas_alarm_level);
}
if(verbose)
{
fprintf(stderr, \"executing %s\\n\", temp);
}
system(temp);
break;
} /* end if c == 10) */
} /* end while getc */
usleep(10000000);
} /* end while */
exit(1);
} /* end function main */
You can compile that code with
gcc -o gas_pic_udp gas_pic_udp.c
And then run it, with ADC alarm level (gas level) set to 100, like this:
gas_pic_udp -l 100 -v
-v for verbose shows you what is happening if run in a terminal
It now forwards the data to an other Raspberry on the LAN, 192.168.78.95 port 1078, alos in UDP packets
That is the main security computer, it record security cameras, audio, air traffic, ship traffic, temperature, humidity, CO level, many more things and now also gas level!
It has an about 4TB hard disc connected to it.. Also plays background music over a speaker if enabled (is always enabled, and here comes the fun
In the next code, on that rapi ..95 I run the following program to listen for the messages from that rapsi ..73.
#include <stdio.h>
#include <string.h>
#include <termios.h>
#include <stdlib.h>
#include <sys/file.h>
#include <errno.h>
#include \"getopt.h\"
#include <math.h>
#include <unistd.h>
#include <ctype.h>
#include <time.h>
#include <stddef.h>
#include <pwd.h>
#include <math.h>
#include <pthread.h>
#include <stdint.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/time.h>
int verbose;
int gas_alarm_level;
#define TEMP_SIZE 65535
pthread_t timer_thread;
pthread_attr_t *attributes;
int timer_running_flag;
int stop_timer_flag;
struct timeval timeout;
time_t now;
time_t ptime = 0;
struct tm *utc_time;
time_t old_ptime = 0;
int elapsed_seconds;
void *timer_routine()
{
time_t now;
if(verbose)
{
fprintf(stderr, \"timer_routine(): arg none\\n\");
}
timer_running_flag = 1;
while(1)
{
if(stop_timer_flag)
{
break;
}
usleep(1000000);
elapsed_seconds++;
if(verbose)
{
fprintf(stderr, \"elapsed_seconds=%d\\n\", elapsed_seconds);
}
if(elapsed_seconds > 30)
{
fprintf(stderr, \"ALARM no signal from gas sensor\\n\");
system(\"amixer sset PCM 100% 1>/dev/zero 2>/dev/zero\");
system(\"mpg123 /root/alarm_geen_signaal_van_gas_detector.mp3 1>/dev/zero 2>/dev/zero\");
usleep(3000000);
system(\"mpg123 /root/alarm_no_signal_from_gas_sensor.mp3 1>/dev/zeroo 2>/dev/zero\");
/* forc alarm voice repeat after 10 seconds */
elapsed_seconds = 20;
} /* end if elapsed_seconds > 30 */
} /* end while */
timer_running_flag = 0;
now = time(0);
fprintf(stderr, \"timer_routine(): exit at %s\\n\", ctime(&now) );
//pthread_exit(0);
return 0;
} /* end function timer_routine */
int start_timer_thread()
{
pthread_create(&timer_thread, 0, &timer_routine, 0);
if(verbose)
{
fprintf(stderr, \"start_timer_thread(): timer_thread_started\\n\");
}
return 1;
} /* end function start_timer_thread */
int main(int argc, char **argv)
{
int a, b, c;
FILE *pptr;
int gas_adc_level;
char temp[TEMP_SIZE];
char rx_string[TEMP_SIZE];
char * ptr;
/* proces any command line arguments */
while(1)
{
a = getopt(argc, argv, \"hl:v\");
if(a == -1) break;
switch(a)
{
case \'h\': // help
print_usage();
exit(1);
break;
case \'v\': // verbose
verbose = 1;
break;
default:
print_usage();
exit(1);
break;
} /* end switch a */
} /* end while command line argumants */
elapsed_seconds = 0;
start_timer_thread();
gas_adc_level = 0;
a = 0;
sprintf(temp, \"netcat -k -u -l -p 1078\");
if(verbose)
{
fprintf(stderr, \"executing=%s\\n\", temp);
}
pptr = popen(temp, \"r\");
// alarm gas 24 1
while(1)
{
c = fgetc(pptr);
if(verbose)
{
fprintf(stderr, \"%c\", c);
}
rx_string[a] = c;
a++;
if(c == 10) // !!! no close, keep reading line by line from netcat
{
a = 0;
if(verbose)
{
fprintf(stderr, \"rx_string=%s\\n\", rx_string);
}
// alarm gas 24 1
ptr = strstr(rx_string, \"alarm\");
if(ptr)
{
fprintf(stderr ,\"ALARM GAS\\n\");
system(\"amixer sset PCM 100%\");
system(\"mpg123 /root/gas_alarm_nl.mp3 1>/dev/zero 2>/dev/zero\");
}
/* reset timer */
elapsed_seconds = 0;
} /* end if c == 10) */
} /* end while getc */
exit(1);
} /* end function main */
So, you can compile that with
gcc -o gas_pic_udp_rx -lpthread gas_pic_udp_rx.c
So what does it DO?
It listens to the MQ4 messages it receives on UDP port 1078, and if it detects the word \'alarm\'
it set the audio mixer volume to maximum using amixer, and then plays audio file /root/gas_alarm_nl.mp3
It is a 50 W amp, and a big speaker, you will notice, all runs on UPS so...
But there was a catch, what if it received no messages from the first Raspberry for whatever reason?
So I added a timer thread, and if it times out on messages received it sets volume again to maximum and plays
/root/alarm_no_signal_from_gas_sensor.mp3
Nice female voices, made with this script using google:
gst5_en
#!/bin/bash
read user_reply
wget -O \"$user_reply\".mp3 \"http://translate.google.com/translate_tts?ie=UTF-8&client=tw-ob&q=$user_reply&tl=en\"
exit 0
you type
gst5_en
Then the text you want spoken foloed by ENTER key
and out comes the mp3 file with that text spoken.
If you wanted Dutch use =nl at the end, =de for German.
So, this code and hardware is released under the GPL licence.
I found an ethernet module as in the wind_thermal thing and a LM2596 switcher chip today in one of the many boxes with electronics here,
so will make a peeseebee and switcher to replace the LM317, plasic box to fit it all in Ifound too too.. PICs I have..
Busy! my new keyboard arrived, so far sound is very good...
https://www.bax-shop.nl/keyboards/fazley-fkb-180-61-toetsen-keyboard-zwart#productreviews
Very cheap, about 120 Euro / dollars.. sound alone is even better than more expensive speakers ...
Different keyboard to hit than this!!!
learning curve...