How to Write a Simple Python Program to Control a Robotic Arm

Robotic Arm Programming has emerged as an integral part of the modern world, influencing industries ranging from manufacturing to healthcare. Harnessing the power of Python, a highly-regarded high-level programming language known for its simplicity and extensive support libraries, it’s possible to unlock the capabilities of these mechanical marvels. This article aims to guide you step-by-step through the process of developing a Python program to control a robotic arm.

Want to try your luck with programming a robot from scratch with a beginner friendly robot kit?  Look no further, check out our Top 5 Best Programmable Robot Kits for Beginners page here:  https://www.awerobotics.com/top-5-best-programmable-robot-kits-for-beginners/.

At the end of the guide, you will find a simple Robotic Arm that you can use to test out what you’ve learned here in terms of programming a Robotic Arm. The section is titled: Test Out What Your Learned With Lego Mindstorms EV3 Kit.

Understanding the Basics of a Robotic Arm

To begin our programming adventure, it’s crucial to grasp the basic principles of a robotic arm. Robotic arms, from the simplest to the most complex ones, share certain common attributes. We’ll examine their components, how they work, and the programming concepts behind them.

Unlock the Secrets of Robotics with Python - Get Your FREE eBook Now!

Unleash the Power of Python in Robotics - Your first step towards mastering robotic programming.

78 Pages Packed with Actionable Insights and Practical Guides.

Screenshot 2023-10-31 071640.png

Just enter your name and email below to receive your free guide.

We respect your email privacy

What is a Robotic Arm?

A robotic arm is a type of mechanical arm which, in most cases, operates similarly to a human arm. The functions of these arms can be as simple or complex as their designs, with applications that can range from manufacturing to space exploration.

Components of a Robotic Arm

At the most basic level, a robotic arm consists of several important components: the base, joints, links, and an end effector. The base, typically attached to a solid surface, provides the foundation upon which the entire arm operates. It might also rotate, providing the arm with additional flexibility. The joints serve as the pivoting points allowing the arm to flex and extend. Links are the rigid sections of the arm connecting its joints, and the end effector is the device at the end of the arm. This could be a simple gripper, a welding torch, a camera, or any other tool depending on the arm’s intended use.

In most complex models, robotic arms also feature additional components such as sensors and actuators. Sensors help the arm understand its environment, providing data on variables like temperature, proximity, and pressure. Actuators, on the other hand, allow the arm to interact with its environment by producing physical movements or actions.

Degrees of Freedom

In the world of robotics, the term “degrees of freedom” (DoF) is frequently used. It refers to the number of independent movements a robot, or in our case, a robotic arm, can make. In a simple model, an arm could have as few as two degrees of freedom, for example, moving up/down and left/right. More complex models could have five, six, or even more degrees of freedom, allowing for movements that closely mimic the flexibility of a human arm.

How Does a Robotic Arm Work?

Understanding the basic working of a robotic arm is crucial before you venture into programming one. The functionality of a robotic arm is a balance of mechanical components and electronic control systems.

Product(s) Highlights & Showcase:

Are you looking for a good Robotic Arm Kit? Then look no further. Both of the options below have been hand selected due to their ease of assembly, versatility, durability and great quality.

Power Source

Regardless of their design or complexity, all robotic arms need a power source to operate. This source powers the motors or hydraulic systems that control the arm’s movements. The most common power source is electricity, but other forms of energy such as compressed air or hydraulic fluid may also be used, particularly in industrial settings.

Control System

A control system is like the brain of a robotic arm. It receives instructions or commands and regulates the power supply to the motors based on these commands. In essence, it controls the movements of the robotic arm. The control system can be a simple manual controller or a complex computer program, such as the one we’ll be developing in Python.

Feedback System

A feedback system is a crucial part of many robotic arms, particularly those designed for precise tasks. It provides information about the arm’s current position or status back to the control system. For instance, a feedback system might use sensors to detect that the arm has reached its intended position, or that an object has been successfully grasped by the end effector.

Understanding Robotic Arm Programming

Programming a robotic arm is the process of designing and implementing a control system that sends the correct commands to the arm. These commands may range from simple movements to complex sequences of tasks.

Low-Level Programming

Low-level programming involves controlling the motors’ power directly. This type of programming usually requires detailed knowledge of the robotic arm’s design, the motor specifications, and the physics of the arm’s movements. It’s typically done in low-level languages like C or even Assembly.

High-Level Programming

High-level programming, on the other hand, involves giving the arm commands like “move to position X” or “pick up object Y”. The control system translates these commands into the necessary motor controls. This form of programming is usually done in high-level languages like Python, and it’s the type of programming we’ll be focusing on in this guide.

Setting Up Your Python Environment

AweRobotics.com - How to Write a Simple Python Program to Control a Robotic Arm

Python is the backbone of our project. Ensuring a properly configured Python environment is the first step towards successfully programming our robotic arm. We’ll be going through the process of installing Python, setting up an Integrated Development Environment (IDE), and verifying our setup.

Installing Python

Python is one of the most popular programming languages today, known for its simplicity and readability, making it ideal for beginners. Yet it’s also powerful and versatile, with a wide range of uses from web development to data analysis to, of course, robotic arm programming.

Download Python

You can download Python directly from the official Python website. As of the time of writing, Python 3.x is the most current version and the one we recommend for this project. It’s worth noting that Python 2.x, while still in use in some legacy systems, has officially reached its end of life and is no longer maintained.

Install Python

Installing Python is a straightforward process. After downloading the installation file from the Python website, you simply run the file and follow the on-screen instructions. One critical step during installation is the option to “Add Python to PATH”. Ensure this option is checked, as it allows you to run Python from any command prompt or terminal window, which will be essential as we proceed with our project.

Setting Up an IDE (Integrated Development Environment)

An Integrated Development Environment, or IDE, is a software application that provides comprehensive facilities to programmers for software development. An IDE typically consists of a source code editor, build automation tools, and a debugger. For Python, there are several good options to choose from.

Choosing an IDE

Choosing the right IDE is largely a matter of personal preference and can depend on factors such as your programming experience, the complexity of your projects, and the resources of your machine. Some popular IDEs for Python include PyCharm, Spyder, and Visual Studio Code. For this project, we recommend Visual Studio Code due to its excellent Python support, user-friendly interface, and extensive range of plugins and extensions.

Installing the IDE

Installing an IDE is typically a straightforward process. For Visual Studio Code, for instance, you simply download the installation file from the official website, run the file, and follow the on-screen instructions. Upon completion, you can launch Visual Studio Code and are ready to start coding.

Verifying Your Python Setup

After installing Python and setting up your IDE, it’s important to verify that everything is working as expected. This can save you from potential headaches down the line.

Running a Python Script

The simplest way to verify your Python setup is to run a Python script. Create a new Python file in your IDE (it should end with the extension “.py”), write a simple command such as print(“Hello, World!”), and run the file. If you see “Hello, World!” printed in the output console, congratulations, your Python setup is working correctly!

Importing Libraries

Another way to verify your Python setup is by importing a library. Python comes with a number of built-in libraries, so try importing one of these, such as math, by writing import math at the top of your Python file. If no error message is displayed when you run the file, it means the library was successfully imported and your Python setup is functioning properly.

Introducing Pygame and PySerial Libraries

AweRobotics.com - Introducing Pygame and PySerial Libraries - Robotic Arm Programming

With a functioning Python environment, we’re now ready to introduce two critical Python libraries for our project: Pygame and PySerial. Pygame will handle our user input, while PySerial will handle our serial communications with the robotic arm. In this section, we’ll discuss what these libraries are, why we need them, and how to install them.

What are Pygame and PySerial?

Python’s strength lies in its extensive collection of libraries. Libraries are pre-written pieces of code that we can use in our projects, saving us the time and effort of writing these functions ourselves. For our project, we’ll be using two libraries: Pygame and PySerial.

Understanding Pygame

Pygame is a Python library designed for creating video games. It provides functionalities for graphics, sound, and most importantly for us, handling user input. We’ll be using Pygame to read the user’s keyboard inputs and translate these into commands for the robotic arm.

Understanding PySerial

PySerial is a Python library that allows for serial communication between the Python program and other devices. Serial communication is a form of data transmission where data is sent one bit at a time, in sequence. We’ll be using PySerial to send our commands from the Python program to the robotic arm over a serial connection.

Why Do We Need Pygame and PySerial?

Both Pygame and PySerial are critical to our robotic arm programming project. Each offers functionalities that are essential for the successful operation of our program.

The Role of Pygame

Pygame plays a crucial role in our project by handling user input. While Python does have built-in functions for reading user input, these are generally limited to reading input from the command line and aren’t suitable for real-time input, which is what we need for controlling our robotic arm. With Pygame, we can easily read real-time keyboard inputs, making it an ideal choice for our project.

The Role of PySerial

PySerial, on the other hand, is necessary for our program to communicate with the robotic arm. Without PySerial, we wouldn’t be able to send our commands from the Python program to the robotic arm. While Python does have built-in functions for communicating over a network, these functions don’t support serial communication, which is the type of communication we need for our project. This is where PySerial comes in.

Installing Pygame and PySerial

Now that we know what Pygame and PySerial are and why we need them, the next step is to install them. Thankfully, Python makes installing libraries quite simple with its built-in package manager, pip.

Using pip to Install Libraries

pip is a package manager for Python. It allows you to install and manage additional libraries and dependencies that are not part of the standard Python library. To install Pygame and PySerial using pip, you simply open a command prompt or terminal window and enter the following commands: pip install pygame and pip install pyserial.

Verifying the Library Installations

After installing Pygame and PySerial, it’s a good idea to verify that the installations were successful. You can do this by creating a new Python file in your IDE, writing import pygame and import serial at the top of the file, and running the file. If no error message is displayed when you run the file, it means the libraries were successfully installed and imported, and you’re ready to start using them in your project.

Creating a Basic Robotic Arm Control Program

With our Python environment set up and our libraries installed, we’re now ready to start programming our robotic arm. We’ll begin by creating a basic control program that maps keyboard inputs to arm commands.

Planning Your Program

Before we start coding, it’s crucial to plan out our program. Planning helps ensure that our code is organized and efficient, and that we don’t overlook any important features or functionalities.

Determining Your Control Scheme

The first step in planning our program is to determine our control scheme. This is how we’ll map our keyboard inputs to arm commands. For example, we might decide that the “w” key will make the arm move forward, the “s” key will make it move backward, and so on.

Identifying Your Arm’s Commands

The second step is to identify the specific commands that our robotic arm understands. These commands will be specific to your particular arm model, so you may need to refer to your arm’s documentation or manufacturer’s website. These commands will likely be simple strings of text or numbers that we’ll send over the serial connection using PySerial.

Writing Your Program

With our program planned, we’re now ready to start writing it. This will involve setting up Pygame and PySerial, creating a loop that constantly checks for user input and sends the corresponding arm commands, and handling any errors that might occur.

Setting Up Pygame and PySerial

We begin our program by importing our libraries and initializing Pygame and PySerial. Pygame is initialized with the pygame.init() function, and PySerial is initialized by creating a new Serial object, specifying the serial port our arm is connected to and the baud rate (speed) of the connection.

Creating Your Input Loop

Next, we create our input loop. This is a loop that constantly checks for user input and sends the corresponding commands to the arm. We can use Pygame’s pygame.event.get() function to get a list of all the events (like key presses) that have happened since the last time we checked, and then iterate through this list, checking for the key events that correspond to our control scheme.

Sending Commands with PySerial

When we detect a key event that corresponds to an arm command, we send that command to the arm using PySerial’s write() function. This function sends data over the serial connection. We’ll need to encode our command as bytes before we can send it, which we can do with Python’s built-in str.encode() function.

Handling Errors and Edge Cases

No program is complete without proper error handling. This involves accounting for and properly handling any errors or edge cases that might occur during the execution of our program.

Handling Pygame Errors

Pygame errors can occur for a number of reasons, such as if a user tries to input a key that we haven’t accounted for in our control scheme. We can handle these errors using Python’s built-in try/except syntax, which allows us to “try” to execute a block of code and then “catch” and handle any errors that occur.

Handling PySerial Errors

PySerial errors can occur if there’s a problem with our serial connection, such as if the arm is disconnected or the specified port is invalid. Like with Pygame, we can handle these errors using try/except. We might also use PySerial’s isOpen() function to check if the connection is open before we try to send a command.

Integrating User Input with Pygame

AweRobotics.com - Integrating User Input with Pygame - Robotic Arm Programming

Now that we have a basic control program, we’re ready to start integrating user input. With Pygame, this is a straightforward process. We’ll walk you through capturing real-time keyboard input and mapping it to control commands for our robotic arm.

Capturing Keyboard Input

In Pygame, capturing keyboard input is achieved using the pygame.key.get_pressed() function. This function returns a list of boolean values representing the state (pressed or not pressed) of each key on the keyboard.

Implementing the get_pressed() Function

To implement the get_pressed() function, we call it inside our input loop. The function will return a list of key states, which we can then iterate through to check which keys have been pressed.

Understanding the get_pressed() Return Value

Understanding the return value of get_pressed() is crucial. The function returns a list where each index corresponds to a specific key, and the value at that index is either True (if the key is currently pressed) or False (if it’s not). Pygame provides constants (like pygame.K_a for the “a” key) that we can use to check the state of specific keys.

Mapping Keyboard Input to Arm Commands

Once we’re capturing keyboard input, the next step is to map this input to our arm commands. This involves writing a series of conditional statements to check which keys are pressed and then send the corresponding command to the arm.

Creating Conditional Statements

For each key in our control scheme, we’ll create a conditional statement that checks if that key is currently pressed. We can use the if keyword in Python to do this, and the constant from Pygame to refer to the specific key. For example, our conditional for the “w” key might look like this: if keys[pygame.K_w]:.

Sending Commands Based on Input

Inside each conditional, we’ll send the corresponding command to the arm using PySerial. This involves calling the write() function and passing our command (encoded as bytes) as the argument. For example, if pressing the “w” key is supposed to make the arm move forward, we might have something like ser.write(‘move_forward’.encode()) inside our “w” key conditional.

Implementing Serial Communication with PySerial

With our keyboard input integrated, we’re ready to focus on the other half of our program: sending commands to the robotic arm. We’ll be using the PySerial library to accomplish this.

Establishing a Serial Connection

The first step in implementing serial communication is to establish a serial connection between our Python program and the robotic arm. This involves creating a new Serial object and specifying the port and baud rate of the connection.

Choosing a Port and Baud Rate

The port and baud rate you choose will depend on your specific arm and how it’s connected to your computer. You may need to refer to your arm’s documentation or the manufacturer’s website to determine the correct values. The port will be a string like ‘COM3’ or ‘/dev/ttyACM0’, and the baud rate will be a number like 9600 or 115200.

Creating a Serial Object

Creating a Serial object is as simple as calling the Serial constructor and passing the port and baud rate as arguments. For example, if our arm is connected to port ‘COM3’ at a baud rate of 9600, we might write ser = serial.Serial(‘COM3’, 9600).

Sending Commands Over Serial

Once we’ve established a serial connection, we can start sending commands to the arm. This involves calling the write() function on our Serial object and passing our command (encoded as bytes) as the argument.

Encoding Commands as Bytes

In Python, strings are encoded as bytes using the str.encode() function. This function returns a bytes representation of the string that we can send over the serial connection. For example, to encode the command ‘move_forward’, we would write ‘move_forward’.encode().

Using write() to Send Commands

The write() function is used to send data over the serial connection. We call this function on our Serial object and pass our encoded command as the argument. For example, to send the ‘move_forward’ command, we would write ser.write(‘move_forward’.encode()).

Closing the Serial Connection

Just as important as opening the serial connection is closing it when we’re done. Leaving a serial connection open can cause issues like data corruption and can prevent other programs from accessing the port.

Using close() to Close the Connection

We close the serial connection by calling the close() function on our Serial object. This function closes the connection and frees up the port for other programs to use. We typically call this function at the end of our program, after our input loop has exited.

Verifying the Connection is Closed

After calling close(), it’s a good idea to verify that the connection is indeed closed. We can do this by calling the isOpen() function on our Serial object. This function returns True if the connection is open and False otherwise. So, after calling close(), we should check isOpen() and make sure it returns False.

Debugging and Testing Your Program

AweRobotics.com - Debugging and Testing Your Program - Robotic Arm Programming

Once we’ve written our program, it’s important to thoroughly debug and test it to ensure it works as expected. Debugging involves finding and fixing any errors in our code, while testing involves running our program and checking that it produces the correct output.

Debugging Your Program

Debugging is an essential part of programming. Despite our best efforts, our code may still contain errors or “bugs” that prevent it from working properly. Debugging involves finding these bugs and correcting them.

Understanding Common Python Errors

Understanding common Python errors can make debugging much easier. Some common errors you might encounter include SyntaxError (when your code doesn’t follow the correct syntax of the Python language), NameError (when you try to use a variable or function that hasn’t been defined), and TypeError (when you try to perform an operation on a value of the wrong type).

Using Your IDE’s Debugging Tools

Most IDEs, including Visual Studio Code, come with built-in debugging tools. These tools can help you identify where in your code an error is occurring. For example, when your program encounters an error, your IDE might highlight the line of code where the error occurred and display an error message describing what went wrong.

Testing Your Program

After debugging our program, it’s important to test it to make sure it’s working as expected. Testing involves running our program and checking its output, as well as making sure it handles edge cases and unexpected input gracefully.

Testing Different Control Inputs

To test our program, we should try a variety of different control inputs. This includes testing each key in our control scheme, as well as combinations of keys. We should also test what happens when we press a key that’s not in our control scheme, to make sure our program handles unexpected input gracefully.

Testing Your Robotic Arm’s Response

In addition to testing our program’s output, we should also test our robotic arm’s response to the commands we send. This involves watching the arm as we run our program and making sure it moves as expected. If the arm isn’t responding as expected, there might be a problem with our arm commands or our serial connection.

Experimenting with More Advanced Features

With a working robotic arm control program, you now have a solid foundation in robotic arm programming. But don’t stop here! There are many more advanced features and functionalities you can add to your program to make it more sophisticated and capable.

Implementing Feedback Mechanisms

One useful feature you might consider adding is a feedback mechanism. This involves programming your robotic arm to send data back to your Python program over the serial connection, which your program can then respond to.

Understanding Robotic Arm Feedback

Robotic arm feedback might include data on the arm’s position, speed, or whether it’s currently in motion or at rest. This data can help your program make more informed decisions about what commands to send. For example, if the arm sends back data indicating that it’s reached its maximum range of motion, your program could stop trying to move the arm in that direction.

Integrating Feedback into Your Program

Integrating feedback into your program involves adding a new section to your input loop that reads data from the serial connection. You can use PySerial’s read() or readline() functions to do this. You’ll also need to update your arm commands to request this feedback data, which may require consulting your arm’s documentation or manufacturer’s website.

Adding More Complex Control Schemes

Another way to extend your program is to add more complex control schemes. For example, instead of mapping each key to a single arm command, you might map each key to a sequence of commands that performs a more complex movement.

Programming Complex Movements

Programming complex movements involves sending a sequence of commands to your robotic arm. For example, to program the arm to pick up an object, you might send a sequence of commands to move the arm to the object’s location, lower the arm, close the arm’s gripper, and then raise the arm.

Mapping Keys to Movement Sequences

To map a key to a movement sequence, you’ll need to modify your control scheme. Instead of mapping each key to a single command, you’ll map it to a list of commands. Then, in your input loop, you’ll iterate through this list and send each command in sequence when the key is pressed.

By exploring these advanced features and experimenting with your own ideas, you can further deepen your understanding of robotic arm programming and take your skills to the next level. Remember, the key to mastering robotic arm programming – or any aspect of programming – is practice, so don’t be afraid to try new things and learn from your mistakes. Happy programming!

Optimizing Your Python Program

AweRobotics.com - Optimizing Your Python Program - Robotic Arm Programming

As your control program for the robotic arm grows in complexity, you may begin to notice performance issues. This section will guide you through a series of strategies to optimize your Python code to ensure it runs as efficiently as possible.

Profiling Your Program

Profiling is a process that helps you understand where your program spends most of its time. This is the first step in optimization since it allows you to focus on the areas that will yield the biggest performance improvements.

Using Python’s Built-In Profiler

Python comes with a built-in module, cProfile, that provides a way to profile your program. This tool will provide you a detailed breakdown of how much time your program spends in each function.

Analyzing Profiling Results

Understanding the output of the profiler is key. The results are usually sorted by the cumulative time spent in each function. The functions at the top of the list are the ones where your program spends most of its time and should be your primary targets for optimization.

Optimizing Python Code

Once you know where to focus your efforts, you can start optimizing your code. Python provides several built-in tools and techniques that can help you to make your code run faster.

Using Built-In Functions and Libraries

Python’s built-in functions and libraries are usually faster than custom code. Whenever possible, try to use these built-in features. For example, using built-in data types like sets and dictionaries can often speed up your code significantly.

Avoiding Unnecessary Operations

Every operation in your code takes some time to execute. Therefore, one of the simplest ways to speed up your code is to remove unnecessary operations. This could include eliminating redundant calculations, using variables to store results that are used multiple times, and avoiding loops where possible.

Implementing Multithreading

For more advanced optimization, you can implement multithreading in your program. Multithreading can help your program do multiple things at once, which can significantly speed up execution for certain tasks.

Understanding Multithreading

In Python, multithreading is a way to run multiple threads (mini-programs) at the same time. This can be particularly useful for tasks that involve waiting, such as reading from or writing to a file, downloading content from the internet, or, in our case, waiting for user input or for the robotic arm to finish moving.

Using Python’s threading Module

Python’s built-in threading module allows you to create and manage threads. You can create a new thread by defining a new Thread object, and you can start it with the start() method. To ensure that your program doesn’t exit before the thread is finished, use the join() method.

Test Out What Your Learned With Lego Mindstorms EV3 Kit

Step 1: Choose Your Robot Arm

AweRobotics.com - How to Write a Simple Python Program to Control a Robotic Arm - Lego Mindstorms EV3 kit

There are many different kinds of robot arms available on the market, so it’s important to choose one that meets your needs. For our purposes, we’ll be using the Lego Mindstorms EV3 Kit (easily found on Amazon.com). This particular kit is perfect for beginners because it’s relatively inexpensive and easy to use. Plus, it comes with everything you need to get started, including the robot arm itself, an infrared remote control, and an easy-to-use programming interface.

Step 2: Set Up Your Robot Arm

Once you have your robot arm, it’s time to set it up. The EV3 kit comes with clear instructions on how to do this, so simply follow the directions in the manual. Once your robot arm is assembled, it should look something like this:

Step 3: Install the EV3 Programming Software

Now that your robot arm is all set up, it’s time to install the programming software. The EV3 programming software is available for free from Lego. Simply go to their website and download the software for your operating system.

Once the software is installed, launch it and you should see a screen that looks something like this:

Step 4: Connect Your Robot Arm to the Computer

Now it’s time to connect your robot arm to the computer. To do this, simply plug one end of the USB cable into the robot arm and the other end into the computer. Once connected, you should see a message on the EV3 programming interface that says “EV3 is connected.”

Step 5: Write Your Python Program

AweRobotics.com - How to Write a Simple Python Program to Control a Robotic Arm - Step 5 Write Your Python Program

With your robot arm all set up and connected to the computer, it’s time to write your Python program. When writing programs for the EV3, it’s important to use a specific library designed for the EV3. The ev3dev.ev3 library is a great way to interact with your LEGO Mindstorms EV3 robot. This library makes it easy to control your robot’s motors and sensors, and it also provides a nice interface for working with the buttons and LCD screen on your robot. The ev3dev-lang-python project provides this library and makes it easy to get started with programming the EV3 in Python.

To get started, simply create a new file and import the ev3dev.ev3 library:

You can do this by adding the following line to the top of your file/script:

import ev3dev.ev3 as ev3

Once you have imported the library, you can start using it to control your robot. For example, the following code will make your robotic arm move forward for 3 seconds:

ev3.LargeMotor(‘outA’).run_timed(time_sp=3000, speed_sp=500)

And the following code will make your robotic arm move back for 3 seconds:

ev3.LargeMotor(‘outA’).run_timed(time_sp=3000, speed_sp=-500)

These are just a few examples to get you started. For more information on using the ev3dev.ev3 library, check out the project’s documentation.

And that’s it! You have now written your first Python program for the LEGO Mindstorms EV3!

Conclusion

In conclusion, writing a Python program to control a robotic arm is a relatively simple process. The most important thing to remember is to use the correct libraries and methods for each step. If you have any questions, please don’t hesitate to Contact Us. We would be more than happy to help you get started.

Unlock the Secrets of Robotics with Python - Get Your FREE eBook Now!

Unleash the Power of Python in Robotics – Your first step towards mastering robotic programming.

78 Pages Packed with Actionable Insights and Practical Guides.

Screenshot 2023-10-31 071640.png

Just enter your name and email below to receive your free guide.

We respect your email privacy