Control of external devices using serial connection¶
This tutorial delves into the control of external devices using serial connection. The serial connection is a communication protocol that enables the communication between two devices. It is widely used in the industry, and it is a very useful tool to build proofs of concepts and early prototypes, particularly in combination with platforms like Arduino or Raspberry Pi.
In many applications, computers are used as a bridge to connect external devices to the world, providing for instance enhanced processing capabilities, better user interfaces, and Internet connectivity to our applications. This simple paradigm is called computer-aided control, it is widely used in the industry, and it is one of the main pillars of the Internet of Things (IoT).
After completing this tutorial you will:
Get an understanding of how serial communication works
Be able to establish a serial connection between an Arduino microcontroller and a Python program
Send and receive data
Troubleshoot common issues in serial communication
So with no further ado, let’s start working! First, let’s get acquainted with the inner workings of serial communication.
Serial Communication Overview¶

Serial communication enables data transfer between two devices where units of data, normally bytes, are sent sequentially, bit by bit, over a single channel. Unlike parallel communication, which transmits multiple bits simultaneously, serial communication sends data, one bit a time and only in one direction, over a single communication channel (normally a wire). This way, the main advantage of serial communication is that it is simpler to implement.
At low level, the way that serial communication works is very simple. Basically, it encodes bits of information in patterns of a physical property of the communication media, like the voltage or current in cable communications or light intensity in infrared communications. For instance, using the Non-Return to Zero Inverted (NRZI) encoding in wired communications, a 0 is transmitted by toggling the voltage of the data line from one state to the other, while a 1 is encoding by keeping the state as-is.

Note that, for this encoding to work, both sender and receiver need to know where each encoded bit (or baud) begins and ends. In this regard, there are two types of serial communication, synchronous serial communication and asynchronous serial communication. The main difference is that in asynchronous serial communications, sender and receiver do not share a clock signal to know when the states begin or end. Instead, they use some kind of communication protocol so that the receiver can learn the baud duration that the transmitter will use, and when the transfer of information will begin and end.
In this tutorial, we will use asynchronous serial communication, which is the standard method for communication between devices used in the Universal Serial Bus (USB) standard.
Thus, asynchronous serial communication typically involves two key aspects:
Baud Rate: The speed of communication, measured in bits per second (bps). Common rates include 9600, 115200, etc.
Data Flow Control: A protocol to ensure data is sent and received correctly, often managed by start/stop bit sequences and parity checks
Back in the day, there were different types of communication interfaces, serial, or parallel, synchronous or asynchronous, to exchange information between different devices. However, over time, industry adopted USB and asynchronous serial communication as a de-facto standard for device to device communications, due to its simplicity and flexibility to adapt to different scenarios, whereas parallel communication is nowadays mostly used inside our computers and processors.

Serial communication template¶
Pre-requisites¶
Since this tutorial is focused on the control of external devices, it is assumed that the reader has some basic knowledge on electronics and microcontrollers. In particular, it is assumed that the reader is familiar with the Arduino platform.
You will also need to run the code in a computer with Python. Unfortunately, platforms like Google Colabs will not work, because they are executing in the cloud and do not have serial communication with your connected devices. So, you need to use your computer, and make sure you have previously installed Python and Visual Code and that you are familiar with the Visual Code environment, following these tutorials:
You will need to install the pyserial library. You can install it by opening the terminal and typing:
pip install pyserial
Or using Visual Code’s GUI terminal as explained in the tutorial.
Introduction¶
This section contains a template you can use to build IoT applications integrating Python 🐍 and the mighty Arduino 🛠️, the master of sensing and controlling the physical world!. The template consists of two parts, the Arduino part, which is the program that will run in the Arduino microprocessor and the Python part, which is the part that will run in your computer.
The following figure illustrates the usage sequence implemented in the template.

The main steps in the sequence are:
Start Arduino: First, the user (to the left) needs to start the Arduino board
Device set-up: Starting the Arduino board (to the middle) will trigger the device set up. The set-up involves initializing the serial communication in the device at a specific baud rate
Set remote mode: Our device will implement a remote mode to enable smooth interaction with a computer. The device will enable remote mode by toggling the remote mode switch.
Start Program: Next, the user will start the Python script in the computer.
Initialization: The Python script (to the right) starts by setting up the environment, importing the libraries used for serial communication, and specifying parameters such as the baud rate, port, and timeouts.
Welcome message: The Python script will show a welcome message to the user, presenting the different options and commands supported by the program
Select command: The user will select one specific command out of the supported commands
Send signal to the device: Once connected, the computer sends a signal (a data packet) over the serial link to the Arduino device to request a specific operation to be executed by the microcontroller. This could involve reading sensor data, calling actuation control commands, or even modifying configuration settings to the microcontroller, whatever your use case requires.
Process request: The microcontroller receives the data and processes it accordingly. As mentioned above, the received signal may trigger specific actions like toggling LEDs, reading sensor values, or altering operation modes.
Send response: The microcontroller uses the serial link to send the results back to the computer.
Process response: The Python program then processes the response, for instance, formatting the result for the user, or even sending new requests to the microcontroller if it is required to complete the user command.
Print results and wait for new commands: Finally the Python program shows the results to the user and waits for the next user command.
The image below shows the hardware configuration that we will use to communicate to the Arduino device using a serial connection.

In this configuration, the Arduino board will be connected to the computer using a USB cable, and it will implement a switch to toggle the remove mode. In remote mode, the user will use the terminal to type in a command, and Python will send it to Arduino using a USB cable as serial connection. Arduino will then read the command and do its thing. For example, If the command is to read the temperature, 🌡 Arduino will read the temperature and send back the value to Python. Back in Python, we will show this information to the user in the terminal. So, in summary, we are going to use the USB cable to send messages between our Arduino program and our Python program as if it was a good old telephone line 📞.
So, how does this work? Well, it’s actually pretty simple. In Python, we will use input() to ask the user for a command. Then, we will use the serial library to send request signals to Arduino (specifically the serial.write() method). And, once we get the information back from Arduino, we will print it using print(). In Arduino, we will use the Serial library and its methods Serial.read() and Serial.write() to read the request from the serial connection and write the
results back to Python. This way the user will use the Python script in the terminal to chat with Arduino. 🤓💬
So, buckle up, and let’s get this party started! With Python and Arduino, we are going to make magic! 🎩✨ The next sections of the tutorial provide a step-by-step guide to implement the strategy using a simple example.
Arduino part¶
Let us start by describing the Arduino part. Remember that, to use Arduino, you need an actual Arduino board connected to your PC using a USB port. Also, you will need to connect sensors or actuators to implement your use case. As described above, in the template, we will use a switch button to toggle the remote mode on and off, and also a LED to know when the device is in remote mode. So please, before you start, make sure you have these components at hand. The next sections will describe the template in detail. You can use this template as a scaffold to develop your own project.
You can download the full template from this link: Arduino template.
☝ Read the comments and code descriptions carefully to understand the code. This template is designed to be flexible and adaptable to various IoT and automation projects. Throughout the template, you’ll find //TODO comments that guide you in customizing the code for your own project. You can replace the sensor readings, commands, and display messages with your specific application logic.
1. Library Imports¶
The first step is to import the necessary libraries to handle our hardware, like sensors, or LCDs, and any serial communication. Remember that the syntax to import libraries in Arduino is #include <Name_of_library.h>
The example below imports some generic libraries:
#include <Adafruit_Sensor.h> // Library for sensor handling
#include <DHT.h> // Library for DHT sensor (temperature and humidity)
#include <LiquidCrystal_I2C.h> // Library for the LCD with I2C communication
The template imports the following libraries:
Adafruit_Sensor: Unified sensor library for handling sensors. You can find the documentation here
DHT: This is a specific library for the DHT11/22 temperature and humidity sensors that we will use to guide the example. Remember to add or replace with the libraries you need to control your own sensors!
LiquidCrystal_I2C: Library to control an LCD over I2C (a communication protocol). If your project does not use an LCD, you can remove this.
2. Pin Configuration and Object Initialization¶
Next, we need to define the pins for your sensors and actuators and initialize the objects needed to interface with the components:
// Pin configuration and sensor type (You can replace or remove depending on your project)
#define DHTPIN 2 // TODO: Set your sensor pin
#define DHTTYPE DHT11 // TODO: Set your sensor type (e.g., DHT11, DHT22)
const int pinLED = 13; // Onboard LED for mode indication (optional)
// Initialize sensor and LCD objects
DHT dht(DHTPIN, DHTTYPE);
LiquidCrystal_I2C lcd(0x27, 20, 4); // TODO: Set your LCD address and size (if applicable)
In the code, DHTPIN and DHTTYPE are used to define the pin connected to the DHT sensor and specify the sensor type (e.g., DHT11 or DHT22). You will need to replace this code to configure your own sensors.
Next, the constant pinLED is used to control the onboard LED used to indicate whether the project is in local or remote mode. It is highly recommended that you use a LED in your project to verify that the basic functionality of switching the remote mode on and off is working.
Finally, the last two lines of the code cell initialize the objects used to interact with the main components of the board:
dht: Initializes the DHT sensor object with the correct pin and type.lcd: Initializes the LCD object. Adjust the address (0x27) and size (20x4) depending on your display.
3. Setup Function¶
Now, recall that the setup() function runs once when you start the Arduino. It initializes communication protocols, sensors, and output devices. The following code cell provides a template for your setup function:
void setup()
{
Serial.begin(9600); // Start serial communication at 9600 baud rate
pinMode(12, INPUT); // TODO: Set your input pin for mode selection (e.g., local/remote)
pinMode(7, OUTPUT); // TODO: Set your output pin for controlling an actuator
digitalWrite(7, HIGH); // TODO: Set default state for your actuator (e.g., pump off)
dht.begin(); // Initialize the DHT sensor (if applicable)
lcd.init(); // Initialize the LCD (if applicable)
lcd.backlight(); // Turn on the LCD backlight
pinMode(pinLED, OUTPUT); // Set the onboard LED as output for indication (optional)
digitalWrite(pinLED, LOW); // Start with LED off
}
Let us explain the code line by line:
Serial.begin(9600)Initializes serial communication at a baud rate of 9600 bps. This is standard baud rate for Arduino projects.pinMode(12, INPUT)Configures pin 12 as an input for selecting between local and remote mode. Remember to update this pin if you use a different pin for the remove mode switch in your board.pinMode(7, OUTPUT)anddigitalWrite(7, HIGH)Configures pin 7 as an output for controlling an actuator (like a pump) and sets it to off initially. Remember to update the pin and repeat this line for every other actuator in your boarddht.begin()Starts the DHT sensor. Remember to replace with the start method of your sensor if it is different from DHT.lcd.init()andlcd.backlight()initializes the LCD and turns on the backlight.pinMode(pinLED, OUTPUT)Prepares the onboard LED for output use as a mode indicator.
4. Main Loop¶
Now recall that loop() function runs continuously and handles the main logic of your Arduino controller, including mode selection, data reading, and serial communication. Let us explain the function used in the template step by step.
4.1. Void loop declaration and main loop actions:¶
The first step is to declare the function and perform any action like reading sensor data that needs to be executed in each loop:
void loop()
{
// Example sensor readings (Customize as needed)
float sensorValue1 = dht.readHumidity(); // TODO: Replace with your sensor reading
float sensorValue2 = dht.readTemperature(); // TODO: Replace with your sensor reading
int sensorValue3 = analogRead(A0); // TODO: Replace with your sensor reading
sensorValue1, sensorValue2 and sensorValue3 are placeholder variables for storing sensor readings. In this example, the first two sensor values represent humidity and temperature from the dhtsensor, whereas analogRead(A0) reads an analog value from pin A0 (useful for soil moisture sensors). Remember to replace with your sensor’s pin and reading methods.
4.2 Mode Selection: Local vs Remote¶
In this section, we determine if the system is in remote or local mode based on the input from pin 12:
// Mode Selection: Remote vs Local
if (digitalRead(12) == HIGH) // TODO: Adjust mode selection criteria as needed
{
digitalWrite(pinLED, HIGH); // Indicate remote mode
lcd.setCursor(0, 0);
lcd.print("Your Project "); // TODO: Customize your display text
lcd.setCursor(0, 1);
lcd.print("Remote mode ");
Recall that digitalRead(12) checks the status of pin 12 to decide if the system should enter remote mode, and digitalWrite(pinLED, HIGH)` turns on the onboard LED to indicate remote mode. Finally,`lcd.setCursorandlcd.print``` are used to display a custom message on the LCD when in remote mode.
4.3 Handling Serial Commands in Remote Mode¶
Now, in remote mode, the system reads and responds to serial commands from the computer. Let us see how in hte next code cell:
// Serial Communication Commands
char option = Serial.read();
if (option == '1')
{
// TODO: Replace with your command handling (e.g., send sensorValue1)
delay(1000);
Serial.print("Sensor 1 Value: ");
Serial.print(sensorValue1);
Serial.print("\n");
}
Serial.read() reads the incoming serial data and stores it in the char variable option. Each value of option will be used to handle a specific command. In the example, the value ‘1’ Handles the user command of sending sensor data back to the computer.
☝ Note that the function
Serial.readreads only one character (char) at a time, and the option variable value will only contain one character. This is important as for instance, you will not be able to encode more commands than those available in the character set you use! Also, note that we always print the new line character “\n” to handle the command by writing a new line in the serial port. We will leverage this in Python to read the device responses in a single line.
For your application, you will need to add as many options as your project requires, and replace each command block with your own logic, depending on the needs of your project.
4.4 Local Mode: Display Data on LCD¶
Finally, if the system is in local mode, it just displays some sensor readings directly on the LCD:
else
{
// Local Mode: Display sensor values on the LCD
digitalWrite(pinLED, LOW); // Indicate local mode
lcd.setCursor(0, 0);
lcd.print("Local mode ");
lcd.setCursor(0, 1);
lcd.print("Sensor 1: " + String(sensorValue1) + " "); // TODO: Customize display
delay(3000);
lcd.setCursor(0, 1);
lcd.print("Sensor 2: " + String(sensorValue2) + " "); // TODO: Customize display
delay(3000);
}
}
digitalWrite(pinLED, LOW) turns off the LED to indicate local mode, lcd.print displays sensor data on the LCD. Make sure you customize this message for your project. Finally, delay(3000) adds a delay to ensure the display is readable.
Python Part¶
Now, let’s take a look at the Python counterpart of the template. you can download the full template from this link: Python Part template Just as before, make sure you read the code carefully and replace or update the code with the functionality you want to implement.
1. Library Imports¶
Just as with the Arduino part, the first step is to import the libraries we will use:
import serial
import time
import random
In the template, we will use the libreary serial to establish a serial connection with Arduino. The library time will allow us to define timestamps to measure time accurately, and we will use the library random to simulate sensor readings by generating random numbers. Let us see these libraries in action in the next code cells
2. Initialize Serial communication¶
The next step is to initialize the serial communication. To do so, we will need to know which serial port of our computer the Arduino port is connected to. We can get this information from our IDE, like Arduino studio.
# TODO: Find out the name of the port you use to connect to Arduino and update the variable definition
port = None
# Initialize serial communications. Set the baud rate to 96000 bps.
if port is not None:
arduino = serial.Serial(port, 9600, timeout=1)
# TODO: Change to False when you are ready to Basic control structure cards notebook.ipynb it with your own device
simulation_mode = True
In the code, the variable port stores the name of the port used to connect to Arduino in our system. Remember to replace None with the actual name of the port you use. Next, the function call serial.Serial(port, 9600, timeout=1) initializes the variable arduino as an object representing your Arduino device.
Finally, the variable simulation_mode is used to toggle the simulation mode on and off. If the variable is set to True the simulation mode is on, and the program will not try to read data from the arduino device. Instead, it will simulate the device, generating random numbers to represent sensor data.
3. Show welcome message¶
Now, we will use show a welcome message and some basic usage instructions to the user using the function print:
# Let's start only if we have initialize the port or if we are working on simulation mode.
if port is not None or simulation_mode:
# Print welcome message.
# Command interfaces can be really nice! let's welcome our user using some ASCII art
print(" _ __ __")
print("| | /| / /__ / /______ __ _ ___")
print("| |/ |/ / -_) / __/ _ \\/ ' \\/ -_)")
print("|__/|__/\\__/_/\\__/\\___/_/_/_/\\__/")
print("Welcome to the Arduino control panel")
# It is important that you provide the user with some basic instructions explaining
# what each command actually does
# In the template, we support two direct commands to Arduino, and one complex workflow
# where we exchange more information with the device.
# Make sure you extend or adapt the command set to your application use case!
# TODO: Define your command list and update the documentation
print("You can use the following commands:")
print("1. Send a command directly to Arduino")
print("2. Send another command to Arduino")
print("3. Start a complex workflow with Arduino")
print("Press Ctrl+C to exit")
The welcome message uses ASCII art to print a nice welcome message in the console. ASCII art is awesome, and there are plenty of free online tools you can use to customize your welcome message.
Next, the script prints a description of the available commands. In this template, we include the following commands:
Command |
Description |
|---|---|
1 |
Send a command directly to Arduino. For instance, Read humidity |
2 |
Send another command directly to Arduino. For instance,Read humidity |
3 |
Start a complex workflow with Arduino. For instance, read a value, process it in Python, and then call an actuator |
Depending on the selected command, the program will send a different character (or sequence of characters) to the Arduino board, which in turn will execute a different option block in the main loop. This way, the user can control the Arduino device using the console commands.
In the template, the two first user commands are sent directly to the Arduino microcontroller. This is the most straightforward way of working with the device: Expose some functionality with an option and then provide the user with a command to select this option. However, we can provide the user with user commands that implement more complex workflows. For instance, we can use Python to read a temperature in our board and also to check the local temperature using some Internet service (for instance using the BeautifulSoup library and a query to Google) and start a heating system only if the external temperature and the device temperature are outside some ranges. This complex workflow would involve sending a request to Arduino to read the temperature, and then another to control the heating system depending on the temperature values. The next sections of the template will explain how we can build such complex workflows.
Finally, the “CTRL+C” key shortcut can be used to terminate the program.
4. Interact with the device¶
4.1 Start main loop and get Input command¶
Now, the template script will enter an infinite loop and will wait for the input command provided by the user:
while True:
command = input("Enter command: ")
4.2 Execute basic commands¶
Once the serial connection is established, we can interact with the device. The first step is to send a command to the device. We will use the following functions:
ser.write(): This function is used to send a command to the device. This function takes as input a string, so we need to convert the command to a string using thestr()function. We will use the binary encoding prefixbto convert the string to a binary string. This is required because theser.write()function only accepts binary strings.ser.flush(): We will use this function to make sure that the command is sent to the device. This function waits until all data is written to the serial port.ser.readline(): This function will allow us to read the data from the device. This function reads a line from the serial port and returns it as a string. Recall that we used the special line character “\n” in Arduino to terminate the response to a command, so the response content will be contained in a single line.
Let us see this functions in action:
if command in ['1', '2']:
if not simulation_mode:
signal = command.encode('utf-8') # Convert the command to a binary string
arduino.write(signal) # Send the command to the device
arduino.flush() # Wait until the command is sent
raw_data = b'' # Init data as empty string
while raw_data == b'': # Wait for Arduino to do its thing and write the response
raw_data = arduino.readline() # Read message
else:
# In simulation mode, we just generate a random number to simulate the device
raw_data = random.randint(0, 100)
# Print the raw_data passed in the response from the device
print(raw_data)
First, we make sure that the command received from the user is one of the supported direct commands (either ‘1’ or ‘2’). Then, if we are not in simulation mode, we use the string method ‘encode’ to convert the input received by the user to a binary string. Recall that our Arduino program expects a single byte (char) as an option, so it is important to encode the received command as a byte. Then, arduino.write(signal) sends the encoded command to Arduino through the serial interface. Now, at
this point, the main loop of the Arduino will receive this signalled option and execute the corresponding response. To receive the response, the Python script first initializes the variable raw_data as an empty binary string, and will enter a while loop to wait for the Arduino part to finalize the execution of the code block and send the response back to Python. Finally, the functionarduino.readline()´´´ reads the response from Arduino and the result is stored in theraw_data```
variable.
If the Simulation mode is on, then the raw_data variable is set to a random number between 0 and 100 using the function call random.randint(0,100).
4.3 Complex workflows¶
Our strategy to implement complex workflows will consist in concatenating basic command executions. In each execution, we will repeat the steps described in the previous section. The template includes a (very basic) complex workflow that consists of sending the commands ‘1’ and ‘2’ in a sequence continuously, combining the results in a data variable:
# You might not want to send the devices directly. For instance, you may want to
# provide info from the device to the user in continuous mode.
# In this template example, we will send commands 1 and 2 and show the results to the user:
print("Entering interacive workflow. Press Ctrl+C to exit")
while True:
time.sleep(1) # Wait for 1 second, it is wise to give Arduino some time to make sure
# it completes the main loop
if not simulation_mode:
# If we are in simulation mode, we will send the commands 1 and 2 and combine
# results
# First we send the first signal
# TODO: Update the following block code with the logic of your own application
signal = b'1'
arduino.write(signal) # Send the command to the device
arduino.flush() # Wait until the command is sent
first_raw_data = b'' # Init data as empty string
while first_raw_data == b'': # Wait for Arduino to do its thing and write the response
first_raw_data = arduino.readline() # Read message
time.sleep(1) # Wait for 1 second
# Now we send the second signal
signal = b'2'
arduino.write(signal) # Send the command to the device
arduino.flush() # Wait until the command is sent
second_raw_data = b'' # Init data as empty string
while second_raw_data == b'': # Wait for Arduino to do its thing and write the response
second_raw_data = arduino.readline() # Read message
else: # If we are in simulation mode, we just generate random data
first_raw_data = random.randint(0, 100)
second_raw_data = random.randint(0, 100)
# Now we combine both responses in one message for the user
data = f"First response: {first_raw_data} and second response: {second_raw_data}"
print(data)
else:
print("Invalid command")
Note that the script enters an infinite loop, and then it sends commands ‘1’ and ‘2’ in sequence to the device, storing the responses in two variables first_raw_data and second_raw_data, and then prints both results.
It is important to leave some time for Arduino to finish at least 1 execution of its main loop. For that reason, we introduce some delay using the time.sleep() function. This function will pause the execution of the script the seconds that are passed as arguments (1 in the example). Make sure you introduce these pauses in your script for a smooth interaction.
Sample project: Greenhouse Control System 🌱💧¶
With this sample project, you will build a simple greenhouse control system using an Arduino, a DHT11 sensor, and an LCD. The goal is to monitor environmental conditions such as temperature, humidity, and soil moisture while providing two operation modes: local and remote.
In local mode, the system displays the current temperature and humidity readings on the LCD, giving you real-time feedback directly on the greenhouse. In remote mode, you can control and monitor the greenhouse from your computer through serial communication. By sending commands, you can retrieve environmental data, activate or deactivate a water pump, and adjust conditions as needed. The system even includes a basic moisture-checking function that automatically waters the plants if the soil is too dry 🌿.
This project is an excellent introduction to serial communication, allowing you to practice using sensors, actuators, and interactive displays. It also demonstrates the power of integrating hardware and software to create smart systems that can be easily monitored and controlled remotely. Whether you’re a hobbyist or an aspiring IoT developer, this project gives you a strong foundation in computer-aided control and environmental monitoring.
You can download the Arduino part from this link: Greenhouse Arduino part Or just copy and paste the cell below:
#include <Adafruit_Sensor.h> // Library for sensor handling
#include <DHT.h> // Library for DHT sensor (temperature and humidity)
#include <LiquidCrystal_I2C.h> // Library for the LCD with I2C communication
// Pin and sensor type configuration
#define DHTPIN 2 // Pin where the DHT sensor is connected
#define DHTTYPE DHT11 // DHT 11 sensor type
const int pinLED = 13; // Pin for onboard LED
// Initializing sensor and LCD objects
DHT dht(DHTPIN, DHTTYPE);
LiquidCrystal_I2C lcd(0x27, 20, 4); // LCD address and size (20x4)
// Setup function runs once when you press reset or power the board
void setup()
{
Serial.begin(9600); // Start serial communication at 9600 baud rate
pinMode(12, INPUT); // Pin for mode selection (local or remote)
pinMode(7, OUTPUT); // Pin for controlling a water pump or similar device
digitalWrite(7, HIGH); // Default: Turn off the water pump
dht.begin(); // Start the DHT sensor
lcd.init(); // Initialize the LCD
lcd.backlight(); // Turn on the LCD backlight
pinMode(pinLED, OUTPUT); // Set the onboard LED as output
digitalWrite(pinLED, LOW); // Start with LED off
}
// Main loop runs continuously after setup
void loop()
{
// Read humidity and temperature from the DHT sensor
float h = dht.readHumidity();
float t = dht.readTemperature();
int soilMoistureValue = 0; // Variable to store soil moisture reading
// Check if the system is in remote mode (button connected to pin 12)
if (digitalRead(12) == HIGH)
{
digitalWrite(pinLED, HIGH); // Turn on the LED to indicate remote mode
lcd.setCursor(0, 0); // Set cursor position on the LCD
lcd.print("EDEM ");
lcd.setCursor(0, 1);
lcd.print("Remote mode ");
// Read the serial input for commands
char option = Serial.read();
// Handle different serial commands based on user input
if (option == '1')
{
// Command 1: Send humidity data
delay(1000);
Serial.print("Humidity: ");
Serial.print(h);
Serial.print(" %\n");
}
else if (option == '2')
{
// Command 2: Send temperature data
delay(1000);
Serial.print("Temperature: ");
Serial.print(t);
Serial.print(" C\n");
}
else if (option == '3')
{
// Command 3: Send both humidity and temperature data
delay(1000);
Serial.print("Humidity: ");
Serial.print(h);
Serial.print(" % ");
Serial.print("Temperature: ");
Serial.print(t);
Serial.print(" C\n");
}
else if (option == '4')
{
// Command 4: Check soil moisture and manage water pump
digitalWrite(7, LOW); // Activate water pump
delay(4000); // Pump runs for 4 seconds
digitalWrite(7, HIGH); // Turn off pump
delay(4000); // Wait for 4 seconds before next action
// Read soil moisture sensor
soilMoistureValue = analogRead(A0);
Serial.print("Soil Moisture: ");
Serial.print(soilMoistureValue);
Serial.print("\n");
// If soil is dry, run the pump briefly
if (soilMoistureValue >= 350)
{
digitalWrite(7, LOW); // Activate pump
delay(2000); // Pump runs for 2 seconds
digitalWrite(7, HIGH); // Turn off pump
}
else
{
digitalWrite(7, HIGH); // Ensure pump stays off when soil is sufficiently moist
}
}
}
else
{
// Local mode: Show readings on the LCD
digitalWrite(pinLED, LOW); // Turn off the LED to indicate local mode
Serial.println("Local mode. Select remote mode to interact.");
lcd.setCursor(0, 0);
lcd.print("Local mode ");
lcd.setCursor(0, 1);
lcd.print("Humidity: " + String(h) + "%");
delay(3000); // Wait 3 seconds before refreshing the display
lcd.setCursor(0, 1);
lcd.print("Temp.: " + String(t) + "C ");
delay(3000);
}
}
You can download the Python part from this link: Greenhouse Python part or just copy and paste teh cell below:
import serial
import time
import random
# Initialize the port variable.
# If you already know the serial port where the Arduino board is connected,
# you need to assign it to this variable, replacing None with the actual name of the port.
# For instance, if your Arduino board is connected to port COM7, you can use the line below
#port = 'COM7'
port = None
# Initialize serial communications. Set the baud rate to 96000 bps.
if port is not None:
arduino = serial.Serial(port, 9600, timeout=1)
# Simulation mode
# Set the variable simulation_mode to True to simulate the connection to the device to Basic control structure cards notebook.ipynb your Python part
# With this mode, you can run some basic tests to make sure your Python program works before you actually connect
# to Arduino.
simulation_mode = True
# Let's start only if we have initialize the port or if we are working on simulation mode.
if port is not None or simulation_mode:
print(" _ __ __")
print("| | /| / /__ / /______ __ _ ___")
print("| |/ |/ / -_) / __/ _ \\/ ' \\/ -_)")
print("|__/|__/\\__/_/\\__/\\___/_/_/_/\\__/")
print("Welcome to the Arduino control panel")
print("You can use the following commands:")
print("1. Read humidity")
print("2. Read temperature")
print("3. Read humidity and temperature")
print("4. Read soil moisture")
print("5. read all in continuous mode")
print("Press Ctrl+C to exit")
while True:
command = input("Enter command: ")
if command in ['1', '2', '3', '4'] and not simulation_mode:
signal = command.encode('utf-8') # Convert the command to a binary string
arduino.write(signal) # Send the command to the device
arduino.flush() # Wait until the command is sent
raw_data = 'b' # Init as empty binary string
while raw_data == 'b':
raw_data = arduino.readline() # Read the data from the device
print(raw_data) # Print the response from the device
elif command == '5':
print("Entering continuous mode. Press Ctrl+C to exit")
while True:
time.sleep(1) # Wait for 1 second
# Let´s put the data in a dictionary.
data_timestamp = time.strftime("%Y-%m-%d %H:%M:%S")
if simulation_mode: # If we are in testing mode, we will simulate the data
data_humidity = random.randint(0, 100)
data_temperature = random.randint(0, 100)
data_soil_moisture = random.randint(0, 1000)
else: # If we are not in testing mode, we will read the data from the device
# First send a signal to read humidity and temperature
signal = b'3'
arduino.write(signal) # Send the command to the device
arduino.flush() # Wait until the command is sent
raw_data = 'b' # Init as empty binary string
while raw_data == 'b':
raw_data = arduino.readline() # Read the data from the device
#Incoming data is in the format b'Humidity: 50.00 % Temperature: 23.00 \n'
# We need to split the string into a list of strings
raw_data = raw_data.decode('utf-8').strip().split(' ')
# Now we need to convert the strings to floats and add them to the dictionary
data_humidity = float(raw_data[1])
data_temperature = float(raw_data[4])
time.sleep(1) # Wait for 1 second
# Now send a signal to read soil moisture
signal = b'4'
arduino.write(signal) # Send the command to the device
arduino.flush() # Wait until the command is sent
raw_data = 'b' # Init as empty binary string
while raw_data == 'b':
raw_data = arduino.readline() # Read the data from the device
# Incoming data is in the format b'Soil Moisture: 350 \n'
# Decode the data and split it into a list of strings
raw_data = raw_data.decode('utf-8').strip().split(' ')
# Get the soil moisture value and store it in the dictionary
data_soil_moisture = float(raw_data[2])
data = f'New measurement: timestamp= {data_timestamp}, humidity: {data_humidity}%, temperature: {data_temperature}, moisture: {data_soil_moisture}'
print(data)
else:
print("Invalid command")