eCO2 Telegram Bot
eCO2 monitor that indicates poor air quality via a TELEGRAM message, a local display and a web interface
This eCO2 monitor indicates poor air quality via a
Hardware
Required are an eCO2-Sensor CCS811 and an ESP32 SoC-module - preferably an M5Stack "ATOM-matrix" or "ATOM lite" with a build-in NeoPixel-strip. The Hardware components get placed on a simple grid board or a breadboard and are powered from a 5 V USB power supply (5 V @ min 500 mA).
Some more details about this project at https://peterneufeld.wordpress.com/2021/09/29/eco2-telegram-bot/
The eCO2-Sensor CCS811
The CCS811 sensor calculates the eCO2-level by meassuring the tVOC (total Volatile Organic Compound) of exhaled air that may be "polluted" by the human lungs. It can not return the absolute CO2-level!
Additionally the sensors sensitivity changes over the time and under different environmental conditions.
This cheap type of sensor searches a relative "good air condition"-baseline by finding the "best air condition" in a longer interval and then assuming the sensor being in fresh unpolluted air at that time .
BUT: The CCS811 does not store this value by itself..
The sensor needs two conditions for giving reliable values:
Software
The BASIC program was created with and runs in ANNEX32 - a BASIC interpreter for ESP32. Minimum required version is V1.435 (version without Bluetooth but with TELEGRAM-support)
The Telegram-BOT will respond to this commands:
Get Your own TELEGRAM_Token
To use the TELEGRAM functions in the BASIC program you first have to create your own TELEGRAM-BOT by following the instructions of BotFather bot in your telegram APP. This will provide your personal TELEGRAM_TOKEN and a bot name for the BASIC-program code. that can be inserted in the BASIC-code lines to set the appropriate variables
The ANNEX32-BASIC code follows here for a better understanding of the functions.
It is also attached as a file eCO2_BOT_V2_2.txt. This file can be pasted into the editor of ANNEX32 and stored into the ESP32 module as an autorun-file in e.g. /default.bas
DO NOT FORGET TO INSERT YOUR OWN TELEGRAM TOKEN
- local NeoPixel LED-Strip, via a
- Web interface and via a
- TELEGRAM message produced by a built-in telegram bot
Hardware
Required are an eCO2-Sensor CCS811 and an ESP32 SoC-module - preferably an M5Stack "ATOM-matrix" or "ATOM lite" with a build-in NeoPixel-strip. The Hardware components get placed on a simple grid board or a breadboard and are powered from a 5 V USB power supply (5 V @ min 500 mA).
Some more details about this project at https://peterneufeld.wordpress.com/2021/09/29/eco2-telegram-bot/
The eCO2-Sensor CCS811
The CCS811 sensor calculates the eCO2-level by meassuring the tVOC (total Volatile Organic Compound) of exhaled air that may be "polluted" by the human lungs. It can not return the absolute CO2-level!
Additionally the sensors sensitivity changes over the time and under different environmental conditions.
This cheap type of sensor searches a relative "good air condition"-baseline by finding the "best air condition" in a longer interval and then assuming the sensor being in fresh unpolluted air at that time .
BUT: The CCS811 does not store this value by itself..
The sensor needs two conditions for giving reliable values:
- a one-time burn-in-time of about 48 hours
- a minimum run-in of about 20 minutes after each cold start.
Software
The BASIC program was created with and runs in ANNEX32 - a BASIC interpreter for ESP32. Minimum required version is V1.435 (version without Bluetooth but with TELEGRAM-support)
- The program measures the eCO2 level once per second.
- The air eCO2 status gets categorized as GREEN, YELLOW or RED condition.
- The build-in NeoPixel-Matrix locally indicates the condition by its color.
- A Webinterface displays the eCO2 value and the category in the (W)LAN.
- The Category and eCO2 value can be requested via TELEGRAM messenger as the program works as a TELEGRAM_BOT
- a TELEGRAM-Alert is send to the last TELEGRAM-chat_id, if condition is indicated as RED.
- The baseline value can be stored manually by pressing the front button of the ATOM-module.
The Telegram-BOT will respond to this commands:
- /e returns eCO2 value and category [GREEN,YELLOW,RED]
- /s stores the baseline in /baseline.txt
- /r restores the baseline from /baseline.txt
- /i returns the local IP-settings of the module
- [any other character] same as /e
Get Your own TELEGRAM_Token
To use the TELEGRAM functions in the BASIC program you first have to create your own TELEGRAM-BOT by following the instructions of BotFather bot in your telegram APP. This will provide your personal TELEGRAM_TOKEN and a bot name for the BASIC-program code. that can be inserted in the BASIC-code lines to set the appropriate variables
The ANNEX32-BASIC code follows here for a better understanding of the functions.
It is also attached as a file eCO2_BOT_V2_2.txt. This file can be pasted into the editor of ANNEX32 and stored into the ESP32 module as an autorun-file in e.g. /default.bas
DO NOT FORGET TO INSERT YOUR OWN TELEGRAM TOKEN
'####################################################################### ' eCO2-Sensor ' ################## '# Shows AIR QUALITY via the estimated concentration of carbon dioxide '# calculated from known TVOC concentration. This is based on the '# assumption that the VOC produced by humans is proportional to their exhaled CO2. ' '# USED HARDWARE: ' - ESP32-device best: M%Stack "ATOM lite" or "ATOM matrix" with buit-in NeoPixel(s) ' - CCS811 eCO2-TVOC-Sensor at I2C-pins of the ESP32 ' - switch-button (e.g. the front button of M5Stack ATOM) ' '# OUTPUT of eCO2-ppm via ... '- NEOPIXEL_LED with at least 1 pixel to indicate the range [GREEN|YELLOW|RED] of eCO2 ppm '- WebInterface to display eCO2-ppm and Condition [GREEN|YELLOW|RED] '- TELEGRAM-BOT message-on-demand with eCO2-ppm and Condition [GREEN|YELLOW|RED] '- TELEGRAM-BOT alert message, if condition reaches RED eCO2-range for more than x measures values '# Saves the BASELINEof the CCS811 as "GOOD-AIR-CONDITION" in file ' /BASELINE.txt if switch-button is pressed for more than 3 seconds '# Sends a TELEGRAM Alert message to currentt CHAT_ID if RED-condition '# TELEGRAM-BOT commands (with or without leading /) : ' /i => return local IP-Settings ' /s => store the current baseline representing "clean-air-condition" ' in file /baeline.txt ' /r => restore the baseline from file /baeline.txt ' /e or [any other character] will return the eCO2-ppm-value ' and the condition [GREEN|YELLOW|RED] '####################################################################### ' Peter Neufeld 2021/09; DB9JG@me.com Version$ = "V2.2" '------SETTINGS------------- RESTORE_BASELINE = 1 '1 => after a run-in time: restores the baseline from data file if available SAVE_BASELINE = 0 '1 => after a run-in time: save current air conditions as the "clean air" baseline SILENT = 0 '1 => suppress all messages 0 => show some status-messages via wlog LOCATION$ = "ROOM 1" 'a description of the sensor location etc TELEGRAM_activated = 1 '1 => activates the Telegram-BOT TELEGRAM_TOKEN$ = "xxxxxxxxxx:AAHQ3qTIj248QCU0ao_GcetggK3l_nhANj4" TELEGRAM_BOTNAME$ = "@xxxxxx_eCO2_BOT" TELEGRAM_MSG_Threshold = 5 'Message if bad-air-condition for too long TELEGRAM_MAX_FAILED_COUNTER = 0 TELEGRAM_MAX_FAILED = 10 TELEGRAM_is_still_running = 0 LL_GREEN = 400 'lower limit for the green LED; usually ~400 LL_Yellow = 1000 'lower limit for the yellow LED; usually ~1000 LL_RED = 1800 'lower limit for the red LED; usually ~1800 HYST = 0.07 'Hysteresis to stabilize the RED alert status LL_RED_HYST = 0 'temporay Hysteresis correction '--------------------------- eCO2 = 0 eTVOC = 0 CONDITION$ = "GREEN" CONDITION_OLD$ = CONDITION$ T$ = time$ STR_eCO2$ = "" CHAT_ID$ = "" gosub SETUP_PERIPHERAL_HARDWARE IF RESTORE_BASELINE = 1 gosub CCS811_RESTORE_BL_FROM_FILE gosub MAKE_WEBPAGE onhtmlreload MAKE_WEBPAGE onHtmlChange MAKE_WEBPAGE IF TELEGRAM_activated gosub TELEGRAM_INIT timer0 1000, MAIN 'this is the main loop wait end '!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! '#################################################################################### '#################################################################################### MAIN: '------------- gosub READ_eCO2 gosub SHOW_eCO2 T$ = time$ STR_eCO2$ = str$(eCO2) if CONDITION$ <> CONDITION_OLD$ then gosub MAKE_WEBPAGE 'change the webpage '===Send a telegram alert message to latest CHAT_ID if too many RED conditions----- IF condition$ = "RED" and CHAT_ID$ <>"" then RED_COUNT = RED_COUNT +1 else 'reset counter if good condition return before reaching threshold RED_COUNT = 0 endif IF RED_COUNT = TELEGRAM_MSG_Threshold then gosub TELEGRAM_send_alert '===------------------------------------------------------------------------------- '++++restore the latest baseline regularly after a run-in time of 10 minutes count=(count +1) mod (10*60) 'if count = (10*60)-1 then gosub CCS811_SAVE_BASELINE_TO_FILE if count = (10*60)-1 gosub CCS811_RESTORE_BL_FROM_FILE '++++-------------------------------------------------------- ''' '---SAVE baseline if frontbutton pressed longer IF pin(FRONT_BUTTON) = PRESSED then PRESSED_COUNT = (PRESSED_COUNT + 1) mod 4 else PRESSED_COUNT = 0 ENDIF IF PRESSED_COUNT = 3 gosub CCS811_SAVE_BASELINE_TO_FILE '--- return '#################################################################################### MAKE_WEBPAGE: '------------- cls autorefresh 1000 'create the textbox in the html page A$ = "" A$ = A$ + "<H1>eCO2 " + LOCATION$ A$ = A$ + "</H1>" A$ = A$ + "Time :" + textbox$(T$) + "<br>" A$ = A$ + "eCO2:" + textbox$(STR_eCO2$) + "<br>" A$ = A$ + |<span style="color:| + CONDITION$ + |">| A$ = A$ + "<H1>Condition: "+ CONDITION$+ "</H1>" A$ = A$ + "</span>" html A$ return '#################################################################################### SHOW_eCO2: '--------- x=5-x 'toggle the brigthness (0 or 5 up) just to show some activity CONDITION_OLD$=CONDITION$ select case eCO2 case 0 to LL_YELLOW 'GREEN R=0 : G=x+20 : B=0 CONDITION$ = "GREEN" LL_RED_HYST = 0 case LL_YELLOW to (LL_RED - LL_RED_HYST)'YELLOW R=x+10 : G=x+10 : B=0 CONDITION$ = "YELLOW" LL_RED_HYST = 0 CASE (LL_RED - LL_RED_HYST)to 99999 'RED R=x+20 : G=0 : B=0 CONDITION$ = "RED" LL_RED_HYST = (LL_RED * HYST) 'set a hysteresis to stabilize the condition End select neo.strip 0, NEO_NUM, R,G,B 'show the condition at NeoPixel-stripe wlog "eCO2 = "; eCO2, " Condition: "; CONDITION$ return '#################################################################################### READ_eCO2: '--------- if ccs811.avail = 1 then a = ccs811.read eCO2 = CCS811.CO2 eTVOC = CCS811.TVOC end if return '#################################################################################### SETUP_PERIPHERAL_HARDWARE: '------------------------- 'I2C for CCS811 i2c.setup 21, 22 'front-button to inizialize a save-baseline-to-file FRONT_BUTTON = 39 'pin39 for build-in button of M5Stack ATOM xxxx PRESSED = 0 'the built-in switch pulls down at ATOM devices PRESSED_COUNT = 0 pin.mode FRONT_BUTTON, input 'initialize the NEOPIXELs NEO_PIN = 27 'NeoPixel data-pin for M5stack "ATOM xxxx" devices NEO_NUM = 1 ' Number of Neopixels for a M5stack "ATOM lite" if bas.device = 103 then '103 is a M5Stack "ATOM matrix" NEO_PIN = 27 NEO_NUM = 25 'Number of Neopixels endif R=2 : G=2 : B=2 'initial colors for neopixel neo.setup NEO_PIN, NEO_NUM neo.strip 0,NEO_NUM,R,G,B 'CCS811 eCO2-Sensor if ccs811.Setup(&h5a) <> 0 then print "CCS811 not found. Program stopped " wlog "CCS811 not found. Program stopped " end !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! endif wlog ccs811.setdrivemode(1) 'update the sensor every second return '#################################################################################### CCS811_SAVE_BASELINE_TO_FILE: '---------------------------- neo.strip 0,NEO_NUM,0,0,150 'set Neopixels blue to indicate reaction BASELINE$ = str$(CCS811.GETBASELINE) File.save "/BASELINE.txt", BASELINE$ wlog "Saved baseline ";BASELINE$ ; " to file /BASELINE.txt" neo.strip 0,NEO_NUM,R,G,B 'Back to old colors return '#################################################################################### CCS811_RESTORE_BL_FROM_FILE: '--------------------------- BASELINE$ = "39103" 'default if no file if file.exists("/BASELINE.txt") then BASELINE$ = File.read$("/BASELINE.txt") if val(BASELINE$) >0 then wlog "CCS811.SETBASELINE returned: "; CCS811.SETBASELINE(val(baseline$)) wlog "Restored baseline ";BASELINE$ ; " from file /BASELINE1.txt" endif endif return '#################################################################################### TELEGRAM_INIT: '------------- WLOG "TELEGRAM_INIT" telegram.settoken TELEGRAM_TOKEN$ telegram.setwait 10 telegram.setmode 0 onwgetasync TELEGRAM_asynco 'Get the update each 10 seconds, to limit internet traffic timer1 10000, TELEGRAM_getMessage return '#################################################################################### TELEGRAM_getMessage: '------------------- WLOG time$;": TELEGRAM_getMessage:" telegram.GetUpdatesAsync return '#################################################################################### TELEGRAM_send_alert: '--------------- 'send an ALERT message to latest seen telegram chat-id 'ATTENTION! No RED-alert-message, if there was no previous request from a client. 'You can provide a fixed chat_id to overcome this if TELEGRAM_is_still_running then return 'to avoid a second call while still running TELEGRAM_is_still_running = 1 onwgetasync off '!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! If CHAT_ID$="" then return tt$ = "RED-ALERT condition for " + LOCATION$ +" at "+ time$ + ": eCO2 = " + str_eCO2$ + " Condition: " + CONDITION$ neo.strip 0,NEO_NUM,30,30,30 'white Neopixels to indicate a TELEGRAM transmission WLOG telegram.sendmessage$(val(CHAT_ID$),tt$ ) WLOG tt$ neo.strip 0,NEO_NUM,R,G,B 'back to former condition TELEGRAM_is_still_running = 0 onwgetasync TELEGRAM_asynco return '#################################################################################### TELEGRAM_asynco: '--------------- 'Receive the messages and respond according to included command-string if TELEGRAM_is_still_running then return TELEGRAM_is_still_running = 1 onwgetasync off '!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! r$ = WGETRESULT$ WLOG "The TELEGRAM_BOT-Service returns at "; Time$; ": "; r$ if instr(lcase$(r$)," failed") then TELEGRAM_MAX_FAILED_COUNTER = TELEGRAM_MAX_FAILED_COUNTER + 1 WLOG "The TELEGRAM_BOT-Service failed; REBOOT countdown initialized : ", str$(TELEGRAM_MAX_FAILED - TELEGRAM_MAX_FAILED_COUNTER) if TELEGRAM_MAX_FAILED_COUNTER = TELEGRAM_MAX_FAILED then WLOG "REBOOT NOW..." reboot endif endif if instr(r$,|"ok"|) then TELEGRAM_MAX_FAILED_COUNTER = 0 c$ = json$(r$, "chat.id") 'get the chat_id if c$ <>"not found" then CHAT_ID$=c$ tt$ = LOCATION$ +" at "+ time$ + ": eCO2 = " + str_eCO2$ + " Condition: " + CONDITION$ text$ = json$(r$, "text") if (text$ <> "not found") then text$=replace$(text$,"/","") ' now the command may, but must not include a leading / select case left$(lcase$(text$),1) case "i" : tt$=" Local IP-setting is " + IP$ case "r" gosub CCS811_RESTORE_BL_FROM_FILE tt$="Restored the BASELINE from file" case "s" gosub CCS811_SAVE_BASELINE_TO_FILE tt$="Saved the BASELINE to file" end select neo.strip 0,NEO_NUM,30,30,30 'white Neopixels to indicate a TELEGRAM transmission WLOG telegram.sendmessage$(val(CHAT_ID$),tt$ ) neo.strip 0,NEO_NUM,R,G,B 'back to former condition end if TELEGRAM_is_still_running = 0 onwgetasync TELEGRAM_asynco return '####################################################################################
Discussion (0 comments)