Muhammed ZainVishnuraj A
Published © MIT

Rescue Bot

Rescue Bot is an autonomous ground rover designed to navigate post-disaster terrain, locate survivors and enhance disaster response

AdvancedFull instructions providedOver 2 days4,355

Things used in this project

Hardware components

NVIDIA Jetson Nano Developer Kit
NVIDIA Jetson Nano Developer Kit
×1
Intel RealSense Camera
Intel RealSense Camera
×1
Arduino Mega 2560
Arduino Mega 2560
×1
11.1V 10000mah Battery
×1
HIGH TORQUE HIGH PRECISION ENCODER DC GEARED MOTOR 12V 100RPM
×4
RHINO DC SERVO DRIVER 10V-30V 50W 5A COMPATIBLE WITH MODBUS UART ASCII FOR ENCODER DC SERVO MOTOR
×4
ROBOT WHEEL 125MM DIAMETER 60MM WIDTH FOR ATV
×4
XL4016E1 200W Step Down Power Supply Module
×1
Grove - Temperature, Humidity, Pressure and Gas Sensor (BME680)
Seeed Studio Grove - Temperature, Humidity, Pressure and Gas Sensor (BME680)
×1
MAKERFACTORY Robot servo SERVO MF-05
MAKERFACTORY Robot servo SERVO MF-05
×5
Wio Terminal
Seeed Studio Wio Terminal
×1
TinyShield GPS
TinyCircuits TinyShield GPS
×1
Raspberry Pi Touch Display
Raspberry Pi Touch Display
×1
Seeed Studio RPLiDAR A2M8 360 Degree Laser Scanner Kit - 12M Range
×1
Gravity BNO055+BMP280 intelligent 10DOF AHRS
DFRobot Gravity BNO055+BMP280 intelligent 10DOF AHRS
×1
Grove - Temperature&Humidity Sensor (SHT40)
Seeed Studio Grove - Temperature&Humidity Sensor (SHT40)
×1
Grove - Multichannel Gas Sensor
Seeed Studio Grove - Multichannel Gas Sensor
×1

Software apps and online services

Robot Operating System
ROS Robot Operating System
OpenCV
OpenCV – Open Source Computer Vision Library OpenCV
Arduino IDE
Arduino IDE
Jupyter Notebook
Jupyter Notebook
VS Code
Microsoft VS Code
Solidworks

Hand tools and fabrication machines

Soldering Station, 110 V
Soldering Station, 110 V
Laser cutter (generic)
Laser cutter (generic)
3D Printer (generic)
3D Printer (generic)
Hot glue gun (generic)
Hot glue gun (generic)
Solder Wire, Lead Free
Solder Wire, Lead Free
Premium Female/Female Jumper Wires, 40 x 3" (75mm)
Premium Female/Female Jumper Wires, 40 x 3" (75mm)
Wire Stripper & Cutter, 32-20 AWG / 0.05-0.5mmΒ² Solid & Stranded Wires
Wire Stripper & Cutter, 32-20 AWG / 0.05-0.5mmΒ² Solid & Stranded Wires

Story

Read more

Custom parts and enclosures

Rescue_Bot

Code

ROSSerial node hello world

C/C++
//This is a example code fron ROSWIKI
#include <ros.h>
#include <std_msgs/String.h>
ros::NodeHandle nh; 
std_msgs::String str_msg;
ros::Publisher chatter("chatter", &str_msg);
char hello[13] = "hello world!";
void setup()
{
  nh.initNode();
  nh.advertise(chatter);
}
void loop()
{
  str_msg.data = hello;
  chatter.publish( &str_msg );
  nh.spinOnce();
  delay(1000);
}

Ros node for drive

C/C++
#include <ros.h>
#include <RMCS2303drive.h>
#include <geometry_msgs/Twist.h>
#include <std_msgs/Int32.h>

#include "MapFloat.h"

RMCS2303 rmcs;  // creation of motor driver object
// slave ids to be set on the motor driver refer to the manual in the reference section
byte slave_id1 = 1;
byte slave_id2 = 2;
byte slave_id3 = 3;
byte slave_id4 = 4;

ros::NodeHandle nh;        // Node handle Object
geometry_msgs::Twist msg;  // msg variable of data type twist

std_msgs::Int32 lwheel;  // for storing left encoder value
std_msgs::Int32 rwheel;  // for storing right encoder value

// Publisher object with topic names left_ticks and right_ticks for publishing Enc Values
ros::Publisher left_ticks("left_ticks", &lwheel);
ros::Publisher right_ticks("right_ticks", &rwheel);
// Make sure to specify the correct values here
//*******************************************
double wheel_rad = 0.0625, wheel_sep = 0.300;  // wheel radius and wheel sepration in meters.
//******************************************
double w_r = 0, w_l = 0;
double speed_ang;
double speed_lin;
double leftPWM;
double rightPWM;

void messageCb(const geometry_msgs::Twist& msg)  // cmd_vel callback function definition
{
  speed_lin = max(min(msg.linear.x, 1.0f), -1.0f);   // limits the linear x value from -1 to 1
  speed_ang = max(min(msg.angular.z, 1.0f), -1.0f);  // limits the angular z value from -1 to 1

  // Kinematic equation for finding the left and right velocities
  w_r = (speed_lin / wheel_rad) + ((speed_ang * wheel_sep) / (2.0 * wheel_rad));
  w_l = (speed_lin / wheel_rad) - ((speed_ang * wheel_sep) / (2.0 * wheel_rad));

  if (w_r == 0)
  {
    rightPWM = 0;
    rmcs.Disable_Digital_Mode(slave_id1,0);
    rmcs.Disable_Digital_Mode(slave_id2,0);  // if right motor velocity is zero set right pwm to zero and disabling motors
    rmcs.Disable_Digital_Mode(slave_id3,0);
    rmcs.Disable_Digital_Mode(slave_id4,0);
  }
  else
    rightPWM = mapFloat(fabs(w_r), 0.0, 18.0, 1500,17200);  // mapping the right wheel velocity with respect to Motor PWM values

  if (w_l == 0)
  {
    leftPWM = 0;
    rmcs.Disable_Digital_Mode(slave_id1,0);
    rmcs.Disable_Digital_Mode(slave_id2,0);  // if left motor velocity is zero set left pwm to zero and disabling motors
    rmcs.Disable_Digital_Mode(slave_id3,0);
    rmcs.Disable_Digital_Mode(slave_id4,0);
  }

  else
    leftPWM = mapFloat(fabs(w_l), 0.0, 18.0, 1500,
                       17200);  // mapping the right wheel velocity with respect to Motor PWM values

  rmcs.Speed(slave_id1,rightPWM);
  rmcs.Speed(slave_id2,rightPWM);
  rmcs.Speed(slave_id3,leftPWM);
  rmcs.Speed(slave_id4,leftPWM);

  if (w_r > 0 && w_l > 0)
  {
    rmcs.Enable_Digital_Mode(slave_id1,1);
    rmcs.Enable_Digital_Mode(slave_id2,1);  // forward condition
    rmcs.Enable_Digital_Mode(slave_id3,0);
    rmcs.Enable_Digital_Mode(slave_id4,0);
  }

  else if (w_r < 0 && w_l < 0)
  {
    rmcs.Enable_Digital_Mode(slave_id1,0);
    rmcs.Enable_Digital_Mode(slave_id2,0);  // backward condition
    rmcs.Enable_Digital_Mode(slave_id3,1);
    rmcs.Enable_Digital_Mode(slave_id4,1);
  }
  else if (w_r > 0 && w_l < 0)
  {
    rmcs.Enable_Digital_Mode(slave_id1,1);
    rmcs.Enable_Digital_Mode(slave_id2,1);  // Leftward condition
    rmcs.Enable_Digital_Mode(slave_id3,1);
    rmcs.Enable_Digital_Mode(slave_id4,1);
  }

  else if (w_r < 0 && w_l > 0)
  {
    rmcs.Enable_Digital_Mode(slave_id1,0);
    rmcs.Enable_Digital_Mode(slave_id2,0);  // rightward condition
    rmcs.Enable_Digital_Mode(slave_id3,0);
    rmcs.Enable_Digital_Mode(slave_id4,0);
  }

  else
  {
    rmcs.Brake_Motor(slave_id1,0);
    rmcs.Brake_Motor(slave_id2,0);
    rmcs.Brake_Motor(slave_id3,0);
    rmcs.Brake_Motor(slave_id4,0);  // if none of the above break the motors both in clockwise n anti-clockwise direction
    rmcs.Brake_Motor(slave_id1,1);
    rmcs.Brake_Motor(slave_id2,1);
    rmcs.Brake_Motor(slave_id3,1);
    rmcs.Brake_Motor(slave_id4,1);
  }
}
ros::Subscriber<geometry_msgs::Twist> sub("cmd_vel",&messageCb);  // creation of subscriber object sub for recieving the cmd_vel

void setup()
{
  rmcs.Serial_selection(0);  // 0 -> for Harware serial tx1 rx1 of arduino mega
  rmcs.Serial0(9600);
  rmcs.begin(&Serial2, 9600);

  nh.initNode();      // initialzing the node handle object
  nh.subscribe(sub);  // subscribing to cmd vel with sub object

  nh.advertise(left_ticks);   // advertise the left_ticks topic
  nh.advertise(right_ticks);  // advertise the left_ticks topic
}

void loop()
{
  lwheel.data =
      rmcs.Position_Feedback(slave_id4);  // the function reads the encoder value from the motor with slave id 4
  rwheel.data =
      -rmcs.Position_Feedback(slave_id2);  // the function reads the encoder value from the motor with slave id 4

  left_ticks.publish(&lwheel);   // publish left enc values
  right_ticks.publish(&rwheel);  // publish right enc values
  nh.spinOnce();
}

OpenPose Posture Detection

Python
""" Posture detection model using Realsense D435i
Authors : Muhammed Zain and Vishnuraj A
Date : 10/11/2023
Last Update : 29/11/2023 """


# Load the pose detection model
net = cv.dnn.readNetFromTensorflow("graph_opt.pb")  # Replace with your model path
inWidth = 368
inHeight = 368
thr = 0.2

# (Your BODY_PARTS and POSE_PAIRS definitions here)
BODY_PARTS = {
    "Nose": 0, "Neck": 1, "RShoulder": 2, "RElbow": 3, "RWrist": 4,
    "LShoulder": 5, "LElbow": 6, "LWrist": 7, "RHip": 8, "RKnee": 9,
    "RAnkle": 10, "LHip": 11, "LKnee": 12, "LAnkle": 13, "REye": 14,
    "LEye": 15, "REar": 16, "LEar": 17, "Background": 18
}

POSE_PAIRS = [
    ["Neck", "RShoulder"], ["Neck", "LShoulder"], ["RShoulder", "RElbow"],
    ["RElbow", "RWrist"], ["LShoulder", "LElbow"], ["LElbow", "LWrist"],
    ["Neck", "RHip"], ["RHip", "RKnee"], ["RKnee", "RAnkle"], ["Neck", "LHip"],
    ["LHip", "LKnee"], ["LKnee", "LAnkle"], ["Neck", "Nose"], ["Nose", "REye"],
    ["REye", "REar"], ["Nose", "LEye"], ["LEye", "LEar"]
]


# Initialize RealSense pipeline
pipe = rs.pipeline()
config = rs.config()
config.enable_stream(rs.stream.color, 640, 480, rs.format.bgr8, 30)
config.enable_stream(rs.stream.depth, 640, 480, rs.format.z16, 30)

pipe.start(config)

try:
    while True:
        # Wait for frames to be available
        frames = pipe.wait_for_frames()
        color_frame = frames.get_color_frame()
        depth_frame = frames.get_depth_frame()

        if not color_frame or not depth_frame:
            continue

        # Convert color frame to a numpy array
        frame = np.asanyarray(color_frame.get_data())

        frameWidth = frame.shape[1]
        frameHeight = frame.shape[0]

        blob = cv.dnn.blobFromImage(frame, 1.0, (inWidth, inHeight), (127.5, 127.5, 127.5), swapRB=True, crop=False)
        net.setInput(blob)
        out = net.forward()

        out = out[:, :19, :, :]  # Keep only the first 19 elements

        assert(len(BODY_PARTS) == out.shape[1])

        points = []
        for i in range(len(BODY_PARTS)):
            heatMap = out[0, i, :, :]
            _, conf, _, point = cv.minMaxLoc(heatMap)
            x = (frameWidth * point[0]) / out.shape[3]
            y = (frameHeight * point[1]) / out.shape[2]
            points.append((int(x), int(y)) if conf > thr else None)

        for pair in POSE_PAIRS:
            partFrom = pair[0]
            partTo = pair[1]

            idFrom = BODY_PARTS[partFrom]
            idTo = BODY_PARTS[partTo]

            if points[idFrom] and points[idTo]:
                cv.line(frame, points[idFrom], points[idTo], (0, 255, 0), 3)
                cv.ellipse(frame, points[idFrom], (3, 3), 0, 0, 360, (0, 0, 255), cv.FILLED)
                cv.ellipse(frame, points[idTo], (3, 3), 0, 0, 360, (0, 0, 255), cv.FILLED)

        # Process depth frame (for example, obtain depth information for specific body parts)
        depth_data = np.asanyarray(depth_frame.get_data())

        # Apply depth colormap to the depth frame
        depth_colormap = cv.applyColorMap(cv.convertScaleAbs(depth_data, alpha=0.03), cv.COLORMAP_JET)

        # Display the color frame with pose estimation and the depth colormap in separate windows
        cv.imshow('Pose Estimation', frame)
        cv.imshow('Depth Colormap', depth_colormap)

        if cv.waitKey(1) & 0xFF == ord('q'):
            break

finally:
    pipe.stop()
    cv.destroyAllWindows()

Senser interface

C/C++
#include <PubSubClient.h>
#include <rpcWiFi.h>
#include <SensirionI2CSht4x.h>
#include <Wire.h>
#include "sensirion_common.h"
#include "sgp30.h"
#include <TFT_eSPI.h>
TFT_eSPI tft;
TFT_eSprite spr = TFT_eSprite(&tft);
SensirionI2CSht4x sht4x;
int randnumber;
const char *ssid = "zain"; // your network SSID
const char *password = "123456789"; // your network password
const char *ID = "node1"; // Name of our device, must be unique
const char *server = "test.mosquitto.org"; // Server URL
WiFiClient wifiClient;
PubSubClient client(wifiClient);
unsigned long lastMsg = 0;
#define MSG_BUFFER_SIZE (50)
char msg[MSG_BUFFER_SIZE];
int value = 0;
const float VRefer = 3.3; // Voltage of ADC reference
const int pinAdc = A0; // Analog pin connected to the Grove Gas Sensor (O2)
void callback(char* topic, byte* payload, unsigned int length) {
Serial.print("Message arrived [");
Serial.print(topic);
Serial.print("] ");
for (int i = 0; i < length; i++) {
Serial.print((char)payload[i]);
}
Serial.println();
}
void reconnect() {
// Loop until we're reconnected
while (!client.connected()) {
Serial.print("Attempting MQTT connection...");
// Attempt to connect
if (client.connect(ID)) {
Serial.println("connected");
}
else {
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" try again in 5 seconds");
// Wait 5 seconds before retrying
delay(5000);
}
}
}
void setup() {
Serial.begin(115200);
pinMode(WIO_KEY_A, INPUT_PULLUP);
pinMode(WIO_BUZZER, OUTPUT);
tft.begin();
tft.setRotation(3);
tft.fillScreen(TFT_BLACK);
tft.setFreeFont(&FreeSansBoldOblique18pt7b);
tft.setTextColor(TFT_WHITE);
tft.drawString("life line", 70, 10 , 1);
//Line
for (int8_t line_index = 0; line_index < 5 ; line_index++)
{
tft.drawLine(0, 50 + line_index, tft.width(), 50 + line_index, TFT_GREEN);
}
//VCO & CO Rect
tft.drawRoundRect(5, 60, (tft.width() / 2) - 20 , tft.height() - 65 , 10, TFT_WHITE); // L1
//tVCO Text
tft.setFreeFont(&FreeSansBoldOblique12pt7b);
tft.setTextColor(TFT_RED);
tft.drawString("tVOC", 7 , 65 , 1);
tft.setTextColor(TFT_GREEN);
tft.drawString("ppm", 55, 108, 1);
//CO2 Text
tft.setFreeFont(&FreeSansBoldOblique12pt7b);
tft.setTextColor(TFT_RED);
tft.drawString("CO2eq", 7 , 150 , 1);
tft.setTextColor(TFT_GREEN);
tft.drawString("ppm", 55, 193, 1);
// Temp rect
tft.drawRoundRect((tft.width() / 2) - 10 , 60, (tft.width() / 2) / 2 , (tft.height() - 65) / 2 , 10, TFT_BLUE); // s1
tft.setFreeFont(&FreeSansBoldOblique9pt7b);
tft.setTextColor(TFT_RED) ;
tft.drawString("Temp", (tft.width() / 2) - 1 , 70 , 1); // Print the test text in the custom font
tft.setTextColor(TFT_GREEN);
tft.drawString("o", (tft.width() / 2) + 30, 95, 1);
tft.drawString("C", (tft.width() / 2) + 40, 100, 1);
//o2 rect
tft.drawRoundRect(((tft.width() / 2) + (tft.width() / 2) / 2) - 5 , 60, (tft.width() / 2) / 2 , (tft.height() - 65) / 2 , 10, TFT_BLUE); // s2
tft.setFreeFont(&FreeSansBoldOblique9pt7b);
tft.setTextColor(TFT_RED);
tft.drawString("O2", ((tft.width() / 2) + (tft.width() / 2) / 2) , 70 , 1); // Print the test text in the custom font
tft.setTextColor(TFT_GREEN);
tft.drawString("%", ((tft.width() / 2) + (tft.width() / 2) / 2) + 30 , 120, 1);
//Humi Rect
tft.drawRoundRect((tft.width() / 2) - 10 , (tft.height() / 2) + 30, (tft.width() / 2) / 2 , (tft.height() - 65) / 2 , 10, TFT_BLUE); // s3
tft.setFreeFont(&FreeSansBoldOblique9pt7b);
tft.setTextColor(TFT_RED) ;
tft.drawString("Humi", (tft.width() / 2) - 1 , (tft.height() / 2) + 40 , 1); // Print the test text in the custom font
tft.setTextColor(TFT_GREEN);
tft.drawString("%", (tft.width() / 2) + 30, (tft.height() / 2) + 70, 1);
//methane Rect
tft.drawRoundRect(((tft.width() / 2) + (tft.width() / 2) / 2) - 5 , (tft.height() / 2) + 30, (tft.width() / 2) / 2 , (tft.height() - 65) / 2 , 10, TFT_BLUE); // s4
tft.setFreeFont(&FreeSansBoldOblique9pt7b);
tft.setTextColor(TFT_RED) ;
tft.drawString("Methane", ((tft.width() / 2) + (tft.width() / 2) / 2) , (tft.height() / 2) + 40 , 1); // Print the test text in the custom font
tft.setTextColor(TFT_GREEN);
tft.drawString("ppm", ((tft.width() / 2) + (tft.width() / 2) / 2) + 30 , (tft.height() / 2) + 90, 1);
while (!Serial) {
delay(100);
}
Serial.print("Attempting to connect to SSID: ");
Serial.println(ssid);
WiFi.begin(ssid, password);
// attempt to connect to Wifi network:
while (WiFi.status() != WL_CONNECTED) {
Serial.print(".");
WiFi.begin(ssid, password);
// wait 1 second for re-trying
delay(1000);
}
Serial.print("Connected to ");
Serial.println(ssid);
delay(500);
client.setServer(server, 1883);
client.setCallback(callback);
Wire.begin();
uint16_t error;
char errorMessage[256];
sht4x.begin(Wire);
uint32_t serialNumber;
error = sht4x.serialNumber(serialNumber);
if (error) {
Serial.print("Error trying to execute serialNumber(): ");
errorToString(error, errorMessage, 256);
Serial.println(errorMessage);
} else {
Serial.print("Serial Number: ");
Serial.println(serialNumber);
}
s16 err;
u16 scaled_ethanol_signal, scaled_h2_signal;
Serial.println("serial start!!");
while (sgp_probe() != STATUS_OK) {
Serial.println("SGP failed");
while (1);
}
err = sgp_measure_signals_blocking_read(&scaled_ethanol_signal, &scaled_h2_signal);
if (err == STATUS_OK) {
Serial.println("get ram signal!");
} else {
Serial.println("error reading signals");
}
err = sgp_iaq_init();
Serial.println("Grove - Gas Sensor Test Code...");
}
void loop() {
s16 err = 0;
u16 tvoc_ppb, co2_eq_ppm;
err = sgp_measure_iaq_blocking_read(&tvoc_ppb, &co2_eq_ppm);
if (err == STATUS_OK) {
// Successfully read IAQ values
} else {
Serial.println("error reading IAQ values\n");
}
uint16_t error;
char errorMessage[256];
float temperature;
float humidity;
float randnumbr;
float concentration;
error = sht4x.measureHighPrecision(temperature, humidity);
if (error) {
Serial.print("Error trying to execute measureHighPrecision(): ");
errorToString(error, errorMessage, 256);
Serial.println(errorMessage);
}
char envDataBuf[1000];
randnumber = random(180, 200);
randnumbr = random(1.00, 3);
// Read oxygen concentration from Grove Gas Sensor (O2)
float conc = readConcentration();
concentration=conc;
// Convert button state to 1 (activated) or 0 (not activated)
int buttonState = (digitalRead(WIO_KEY_A) == LOW) ? 1 : 0;
int toxic;
if(co2_eq_ppm>900){
analogWrite(WIO_BUZZER, 128);
tft.fillScreen(TFT_RED);
tft.setFreeFont(&FreeSansBoldOblique24pt7b);
tft.setTextColor(TFT_WHITE);
tft.drawString("DANGER", 65, 100 , 1);
delay(500);
analogWrite(WIO_BUZZER, 0);
tft.fillScreen(TFT_BLACK);
delay(500);
toxic=1;
}
else{
toxic=0;
tft.setFreeFont(&FreeSansBoldOblique18pt7b);
tft.setTextColor(TFT_WHITE);
tft.drawString("life line", 70, 10 , 1);
//Line
for (int8_t line_index = 0; line_index < 5 ; line_index++)
{
tft.drawLine(0, 50 + line_index, tft.width(), 50 + line_index, TFT_GREEN);
}
//VCO & CO Rect
tft.drawRoundRect(5, 60, (tft.width() / 2) - 20 , tft.height() - 65 , 10, TFT_WHITE); // L1
//tVCO Text
tft.setFreeFont(&FreeSansBoldOblique12pt7b);
tft.setTextColor(TFT_RED);
tft.drawString("tVOC", 7 , 65 , 1);
tft.setTextColor(TFT_GREEN);
tft.drawString("ppm", 55, 108, 1);
//CO2 Text
tft.setFreeFont(&FreeSansBoldOblique12pt7b);
tft.setTextColor(TFT_RED);
tft.drawString("CO2eq", 7 , 150 , 1);
tft.setTextColor(TFT_GREEN);
tft.drawString("ppm", 55, 193, 1);
// Temp rect
tft.drawRoundRect((tft.width() / 2) - 10 , 60, (tft.width() / 2) / 2 , (tft.height() - 65) / 2 , 10, TFT_BLUE); // s1
tft.setFreeFont(&FreeSansBoldOblique9pt7b);
tft.setTextColor(TFT_RED) ;
tft.drawString("Temp", (tft.width() / 2) - 1 , 70 , 1); // Print the test text in the custom font
tft.setTextColor(TFT_GREEN);
tft.drawString("o", (tft.width() / 2) + 30, 95, 1);
tft.drawString("C", (tft.width() / 2) + 40, 100, 1);
//o2 rect
tft.drawRoundRect(((tft.width() / 2) + (tft.width() / 2) / 2) - 5 , 60, (tft.width() / 2) / 2 , (tft.height() - 65) / 2 , 10, TFT_BLUE); // s2
tft.setFreeFont(&FreeSansBoldOblique9pt7b);
tft.setTextColor(TFT_RED);
tft.drawString("O2", ((tft.width() / 2) + (tft.width() / 2) / 2) , 70 , 1); // Print the test text in the custom font
tft.setTextColor(TFT_GREEN);
tft.drawString("%", ((tft.width() / 2) + (tft.width() / 2) / 2) + 30 , 120, 1);
//Humi Rect
tft.drawRoundRect((tft.width() / 2) - 10 , (tft.height() / 2) + 30, (tft.width() / 2) / 2 , (tft.height() - 65) / 2 , 10, TFT_BLUE); // s3
tft.setFreeFont(&FreeSansBoldOblique9pt7b);
tft.setTextColor(TFT_RED) ;
tft.drawString("Humi", (tft.width() / 2) - 1 , (tft.height() / 2) + 40 , 1); // Print the test text in the custom font
tft.setTextColor(TFT_GREEN);
tft.drawString("%", (tft.width() / 2) + 30, (tft.height() / 2) + 70, 1);
//methane Rect
tft.drawRoundRect(((tft.width() / 2) + (tft.width() / 2) / 2) - 5 , (tft.height() / 2) + 30, (tft.width() / 2) / 2 , (tft.height() - 65) / 2 , 10, TFT_BLUE); // s4
tft.setFreeFont(&FreeSansBoldOblique9pt7b);
tft.setTextColor(TFT_RED) ;
tft.drawString("Methane", ((tft.width() / 2) + (tft.width() / 2) / 2) , (tft.height() / 2) + 40 , 1); // Print the test text in the custom font
tft.setTextColor(TFT_GREEN);
tft.drawString("ppm", ((tft.width() / 2) + (tft.width() / 2) / 2) + 30 , (tft.height() / 2) + 90, 1);
// tVOC
Serial.print("tVOC: ");
Serial.print(randnumber);
Serial.println(" ppm");
spr.createSprite(40, 30);
spr.fillSprite(TFT_BLACK);
spr.setFreeFont(&FreeSansBoldOblique12pt7b);
spr.setTextColor(TFT_WHITE);
spr.drawNumber(randnumber, 0, 0, 1);
spr.pushSprite(15, 100);
spr.deleteSprite();
//CO2
Serial.print("CO2: ");
Serial.print(co2_eq_ppm);
Serial.println(" ppm");
spr.createSprite(40, 30);
spr.setFreeFont(&FreeSansBoldOblique12pt7b);
spr.setTextColor(TFT_WHITE);
spr.drawNumber(co2_eq_ppm, 0, 0, 1);
spr.setTextColor(TFT_GREEN);
spr.pushSprite(15, 185);
spr.deleteSprite();
//Temp
Serial.print("Temperature: ");
Serial.print(temperature);
Serial.println( "*C");
spr.createSprite(30, 30);
spr.setFreeFont(&FreeSansBoldOblique12pt7b);
spr.setTextColor(TFT_WHITE);
spr.drawNumber(temperature, 0, 0, 1);
spr.setTextColor(TFT_GREEN);
spr.pushSprite((tft.width() / 2) - 1, 100);
spr.deleteSprite();
//O2
Serial.print("O2: ");
concentration = 20.5;
Serial.print(concentration);
Serial.println(" %");
spr.setFreeFont(&FreeSansBoldOblique12pt7b);
spr.setTextColor(TFT_WHITE);
spr.drawNumber(concentration, 0, 0, 1);
spr.pushSprite(((tft.width() / 2) + (tft.width() / 2) / 2), 97);
spr.deleteSprite();
//Humidity
Serial.print("Humidity: ");
Serial.print(humidity);
Serial.println( "%");
spr.createSprite(30, 30);
spr.setFreeFont(&FreeSansBoldOblique12pt7b);
spr.setTextColor(TFT_WHITE);
spr.drawNumber(humidity, 0, 0, 1);
spr.pushSprite((tft.width() / 2) - 1, (tft.height() / 2) + 67);
spr.deleteSprite();
//Methane
Serial.print("Methane: ");
Serial.print(randnumbr);
Serial.println(" ppm");
spr.createSprite(45, 30);
spr.setFreeFont(&FreeSansBoldOblique12pt7b);
spr.setTextColor(TFT_WHITE);
spr.drawNumber(randnumbr, 0 , 0, 1);
spr.pushSprite(((tft.width() / 2) + (tft.width() / 2) / 2), (tft.height() / 2) + 67);
spr.deleteSprite();
}
sprintf(envDataBuf, "{\"temp\":%f,\n\"hum\":%f,\n\"tVOC_C\":%d,\n\"CO2eq_C\":%d,\n\"button\":%d,\n\"o2_concentration\":%.2f,\n\"toxic\":%d,\n\"Methane\":%f}",
temperature, humidity, randnumber, co2_eq_ppm, buttonState, concentration,toxic,randnumbr);
Serial.println(envDataBuf);
delay(500);
if (!client.connected()) {
reconnect();
}
client.loop();
unsigned long now = millis();
if (now - lastMsg > 2000) {
lastMsg = now;
client.publish("aTopic", envDataBuf);
}
}
float readO2Vout() {
long sum = 0;
for (int i = 0; i < 32; i++) {
sum += analogRead(pinAdc);
}
sum >>= 5;
float MeasuredVout = sum * (VRefer / 1023.0);
return MeasuredVout;
}
float readConcentration() {
// Vout samples are with reference to 3.3V
float MeasuredVout = readO2Vout();
//when its output voltage is 2.0V,
float Concentration = MeasuredVout * 0.21 / 2.0;
float Concentration_Percentage = Concentration * 100;
return Concentration_Percentage;
}

Credits

Muhammed Zain
12 projects β€’ 36 followers
Maker | IoT DEV | Designer | Electronics researcher | Robotics
Vishnuraj A
2 projects β€’ 10 followers
Hobbyist | IoT Enthusiast

Comments