175 × 235 SPINE: 11.4 FLAPS: 0
PYTHON 3 PROGRAMMING AND GUIS
Andrew Pratt This is the second edition of a book aimed at engineers, scientists and hobbyists who want to interface PCs with hardware projects using graphical user interfaces. Desktop and web based applications are covered.
He continued his career working in industrial controls. Currently he is an instructor teaching industrial control systems.
One project involves a PIC microcontroller with code provided that can be loaded into the PIC using the Uno. The web applications and server are all implemented in Python allowing you to access your electronic hardware over the Internet. The Raspberry Pi computer can be used as your web server. An introductory chapter is provided to get you started with using Linux. The book is written for use with Debian or variations including Mint or Ubuntu. All of the programs in the book are freely available, ready to use and experiment with by way of a download from Elektor.
PYTHON 3
PROGRAMMING AND GUIS
GUIS • Andrew Pratt
He holds a Higher National Certificate in electrical and electronic engineering and a Degree from the Open University.
Hardware interfacing is achieved using an Arduino Uno as a remote slave. A full description and source code of the communication interface is given in the book. The slave provides digital and analogue input and outputs. Multiple Unos can be included in one project with all control code written in Python and running on a PC
FOR ELECTRONIC ENGINEERS
AND
Andrew Pratt served for 25 years in the Royal Air Force as an Aircraft Technician.
The programming language used is Python 3 which is one of the most popular languages around: speed of programming being a key feature. The book has been revised and updated with emphasis on getting the user to produce practical designs with ease - a text editor is all that is required to produce Python programs.
PYTHON 3 - PROGRAMMING
FOR ELECTRONICS ENGINEERS
ISBN 978-1-907920-61-5
LEARN
www.elektor.com
DESIGN
Elektor International Media BV
Andrew Pratt LEARN
DESIGN
SHARE
SHARE
LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIG
N • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHA SIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN RN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SH
Table of Contents
Table of Contents Preface to the Second Edition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 Chapter 1 Linux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 1.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 1.2 Getting the Operating System . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 1.3 Finding Your Way Round . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 1.4 File and Directory Handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 1.5 Wild Cards . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 1.6 Redirection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 1.7 Working with the Python Programs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 1.8 File Permissions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 1.9 Bash Scripts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 1.10 The X-Server . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20 Chapter 2 Getting Started with Python . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 2.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 2.2 Installing Python 3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 2.3 Start Programming . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 2.4 IDLE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24 2.5 Interactive Sessions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24 2.6 Data Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24 2.6.1 Integers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24 2.6.2 Floats . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 2.6.3 Strings and Writing To and Reading from Files . . . . . . . . . . . . . . . . . . . . . . 26 2.6.4 Lists . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36 2.6.5 Dictionaries . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40 2.6.6 Tuples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42 2.7 Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42 2.8 Imports . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44 2.9 Logic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46 2.10 Bit and Bytes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46 2.11 Comparison Operators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
●5
Python 3: Programming and GUIs for Electronic Engineers 2.12 Hexadecimal Notation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49 2.13 Persistence of Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50 2.14 Compiled Python Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51 2.15 Unicode Strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51 2.16 Bytes and Bytearrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54 2.17 Encoding and Decoding Bytes and Unicode . . . . . . . . . . . . . . . . . . . . . . . . . . . 56 2.18 Variable names . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57 Chapter 3 Graphic User Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59 3.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59 3.2 Tkinter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59 3.2.1 Click to Start Programs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60 3.3 Adding Widgets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61 3.4 Callbacks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61 3.5 Grid Manager Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62 3.5.1 Simple Layout . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62 3.5.2 Nested Layout . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64 3.6 Inputting and Outputting Data with Widgets . . . . . . . . . . . . . . . . . . . . . . . . . . . 65 3.6.1 Scale Widget and Label . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66 3.6.2 Entry . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67 3.6.3 Control Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68 3.6.4 Check Buttons . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68 3.6.5 Radio Buttons . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69 3.6.6 The Canvas Widget . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70 Chapter 4 Object Oriented Programming . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73 4.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73 4.2 Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73 4.3 Inheritance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75 4.4 Summary of Object Orientation (OO) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79 Chapter 5 The Arduino Uno as a Slave . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81 5.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81 5.2 The Master Slave Design . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81 5.2.1 USB Serial . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
●6
Table of Contents 5.2.2 Uploading the Arduino Program . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81 5.2.3 Configuring the Arduino with its Address and Project ID . . . . . . . . . . . . . . . 82 5.3 The Uno Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84 5.3.1 Creating Instances of Slaves . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85 5.3.2 Writing to Digital Output Pins . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85 5.3.3 Reading from Digital Input Pins . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86 5.3.4 Writing to Analogue Output Pins . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87 5.3.5 Reading from Analogue Input Pins . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87 5.3.6 Summary of the IO Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87 5.4 Practical Examples Using Hardware . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88 5.4.1 Write to Digital Outputs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88 5.4.2 Write to Analogue Outputs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91 5.4.3 Read from Digital Inputs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92 5.4.4 Read from Analogue Inputs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93 5.5 The Slave State Diagram and the Communications Protocol . . . . . . . . . . . . . . . . 95 5.5.1 The Slave State Diagram . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95 5.5.2 Control of Traffic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96 5.5.3 The Telegram Formats . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97 5.6 The Python End of the Communications Protocol . . . . . . . . . . . . . . . . . . . . . . . . 99 Chapter 6 Further Examples of GUIs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101 6.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101 6.2 A Graph Plotting Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101 6.2.1 Passing Arguments to a Function without Specifying How Many (*args) . . . 102 6.2.2 Passing Arguments to a Function with Keyword Arguments . . . . . . . . . . . . 102 6.2.3 Binding Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103 6.2.4 The Module __name__ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103 6.2.5 Following the Code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104 6.3 CR Charge Discharge Experiments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109 6.4 Plotting Transistor Characteristics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111 6.4.1 Threading . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116 Chapter 7 Bit Map Graphics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119 7.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119
●7
Python 3: Programming and GUIs for Electronic Engineers 7.2 The Bit Map File . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119 7.3 Reading and Writing to Binary Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121 7.4 The Image . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122 7.5 The Modified Image . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124 7.6 The Header . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125 7.6.1 The Struct Module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126 7.7 Junk Bytes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128 7.8 Class BmpDraw . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128 Chapter 8 A Project to Monitor Your Electricity Consumption . . . . . . . . . . . . . . . . . 131 8.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131 8.2 The Probe and Remote Sensing Head . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131 8.3 The PIC Microcontroller . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133 8.3.1 The Arduino Slave as a PIC Programmer . . . . . . . . . . . . . . . . . . . . . . . . . 134 8.4 The Radio Link and the Arduino Slave . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135 8.5 Commissioning the System and the Desktop Program. . . . . . . . . . . . . . . . . . . . 135 8.5.1 The Desktop Calibration Program . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136 Chapter 9 Web Browser Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145 9.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145 9.2 HTML 5 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145 9.3 CherryPy a Python Web Framework . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146 9.3.1 User Input using Web Forms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 148 9.4 The Ammeter and Current Trend Web Applications . . . . . . . . . . . . . . . . . . . . . 150 9.4.1 The Uno Slave Program . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150 9.4.2 The CherryPy Program . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151 9.5 The Raspberry Pi as a Web Server . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157 9.5.1 Configuring Wi-Fi from the Command Line on the Pi . . . . . . . . . . . . . . . . 158 9.5.2 Running the Python Programs Automatically After Boot. . . . . . . . . . . . . . . 159 9.6 Access from the Internet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160 9.7 Adapting this Project . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161 Chapter 10 Curses Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163 10.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163 10.2 Getting Started with Curses . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163
●8
Table of Contents 10.2.1 The Wrapper Function . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164 10.2.2 Windows on the stdscr . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164 10.2.3 Colour . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165 10.3 Custom Widgets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 169 10.3.1 An Indicator Lamp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 169 10.3.2 An Analogue Gauge Widget . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 172 10.3.3 Combining Attributes with the Bitwise OR Operator . . . . . . . . . . . . . . . . 173 10.3.4 Mouse Events for User Input . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173 10.3.5 A Thumbwheel Widget . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 174 10.3.6 A Button Widget . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 178 10.3.7 A Label Widget . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 179 10.4 The Module with all the Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 180 Appendix A Arduino Uno Slave Source Code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 185 Appendix B The Python Uno Slave . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193 B.1 Pydoc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 199 Appendix C The Arduino Slave Programming Tool . . . . . . . . . . . . . . . . . . . . . . . . . 201 Appendix D The Arduino Slave ID Setting Tool Source Code . . . . . . . . . . . . . . . . . . 203 Appendix E The PIC Source Code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 207 Appendix F The PIC Programming Tool . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 209 F.1 12F1622_PROGRAMMER_V1.0.py . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 209 Appendix G The Curses Widgets Module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 215 G.1 Curses_widgets.py . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 215 Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 219
●9
Preface to the Second Edition
Preface to the Second Edition This book is intended to be a practical guide to interfacing simple hardware projects with a PC. There are many topics covered in the book. They are not a random selection and have been included as each is required to achieve the aim of communicating with a particular interface device which has been selected for its availability and low cost. The device is the readily available Arduino Uno. As this book is about Python, an Arduino Sketch is provided that allows the reader to use Unos as slaves. The Python programs run on a Linux PC to control external electronic circuits through the Unos’ inputs and outputs. More than one of these slaves can be included in each project as each is given a unique address. By the end of the book you will be able to control and monitor hardware from your own desktop graphic user interface (GUI) or web browser over the Internet. To do this, the book will explain how to program in Python, write web pages, and use Python as a web server. Some electronic circuits will be presented to integrate with the software projects. One of the projects will be to monitor the current consumption of a building or house. Another project will plot the characteristics of a transistor. Also an introduction to Linux is given. The chosen distribution is Debian but any of its derivatives such as Ubuntu, Mint or Raspbian if you want to use the Raspberry Pi will do. The first edition of this book made use of a customised live Linux CD with all the programs and necessary software on it. However in the time the first edition was in print, this distribution became out of date so it is felt that you should always use a current distribution. The exercise programs are available as a download from the Elektor website. Debian has been chosen because of its ease of installing applications, stability and popularity. It should be appreciated that none of the topics are covered in depth. However enough information is provided to achieve the aim and give the reader a start in each of these. Each chapter that deals with a new topic will not try to cover everything in that chapter, but subsequent chapters will add to those topics as required. There is a huge amount of documentation available on the Internet. The problem is knowing in which direction to go to get started as practical projects involve a range of topics. This book should give you a starting level that you can build on. It assumes the reader is familiar with using a computer but no knowledge of programming is expected. A basic knowledge of electricity and discrete electronic components such as resistors, capacitors, switches, and LEDs is required. An ability to solder will be useful but the projects can be tried out using solderless breadboards. Breadboards can be cheaper overall because components and wire can be reused. Also construction is much quicker. This is the second edition of the book and there are four major changes: Python 3 is used throughout instead of Python 2, the Arduino Uno board is the target hardware interface instead of the FTDI FT245 development board, a standard Debian type distribution is used, and instead of using the Apache Web Server, a Python web framework is used for serving web pages. I hope you find this book interesting and fun.
â—? 11
Chapter 1 • Linux
Chapter 1 • Linux 1.1 • Introduction Linux has been chosen as the OS (operating system) because as you will see as you read the book you have so much freedom to change things and experiment. If you are already an experienced Linux user and are used to using the command line you will be familiar with the contents of this chapter. You might use Linux but never venture away from the graphic interfaces provided or perhaps you are completely new to Linux. The examples shown in this book were developed using Debian 8 Jessie, the current stable release. There are several Debian based distributions available. Two popular examples are Mint and Ubuntu and if you want to use a Raspberry Pi there is Raspbian.
1.2 • Getting the Operating System Here I am assuming that you either have or want to install a Debian based OS. If this is not the case you could try using a virtual machine such as VirtualBox that will allow you to keep your existing OS, however this might run slower than is desired. At the time of writing, Debian can be obtained by download from "https://www.debian.org/distrib/netinst". It is not difficult to install.
1.3 • Finding Your Way Round If you are new to Linux things are going to seem strange at first, particularly the file directories. Throughout the book we are going to be using the command line to access files and start applications etc. This might seem like a backward step, but the Linux command line is full of quick short cuts and time saving tricks. Also while writing programs the same command line statements will sometimes need to be put into the code. Find on your Desktop or Applications Menu for your OS the icon for a "Terminal Emulator"
Figure 1-1 Terminal Emulator Figure 1-1 shows that we are in our home directory, the ~ means home directory. This is the home directory of the user named "user". As an introduction to the command line, type "cd /" without the quotation marks. Press return, then type "ls" and press return. The result is shown below; directories show up in blue. There are many commands available for different tasks and you quickly learn the common ones. "cd" means change directory and "ls" means list the contents of a directory.
● 13
Chapter 2 • Getting Started with Python
Chapter 2 • Getting Started with Python 2.1 • Introduction Microprocessors handle numbers. These numbers are represented physically by using the binary system implemented as voltages. When a computer handles text it is still handling numbers. For example the character "A" is 65 in the American Standard Code for Information Interchange (ASCII). One of the changes between Python 2 and 3 is the way text is encoded. ASCII used to be the way text was encoded under Python 2 by default. Python 3 uses Unicode; the difference will be dealt with at the end of the chapter. Unicode is a very important topic that needs to be understood. At a most basic level, a microprocessor could have its code written by a human in machine code, which are the numbers that represent the instructions and data. An improvement is to use an assembler, which is a program that accepts human friendly abbreviations for the machine code and writes a file that is the machine code. On the left, Program 2-1a is a short program written for the assembler FASM. Program 2-1 Example of assembler and 'C' coding ;prog_02_01.asm
//prog_02_01.c
format ELF executable jmp main message db "It’s a lot easier in Python!",10 len equ $-message
#include<stdio.h> int main(){ printf("It's a lot easier in python\n");
main: mov edx, len mov ecx, message mov eax, 4 int 0x80
return 0; }
mov eax, 1 int 0x80; Prog 2-1a (prog_0201.asm)
Prog 2-1b (prog_02_01.c)
The source code prog_02_01.asm has been assembled to an executable file assembler_demo and is on the zip file. If you want to, you can run it at the command line as follows (make sure you are in the same directory as the file and then type ./ assembler_demo, actually ./a tab should then auto-complete for you): user@debian:~/programs/chap02_progs$ ./assembler_demo It’s a lot easier in python! user@debian:~/programs/chap02_progs$
Assembler is still used where the utmost speed is important or in applications such as small micro-controllers with limited memory. It requires knowledge of the microprocessor’s architecture and other hardware details by the programmer and the code is not portable to other platforms. Program 2-1b does the same as the assembler program using C. C uses a program called a compiler that allows you to write in a form that is more readable and deals with the details of the hardware. The compiler takes the C source code written by a human and compiles it into executable files that can be run by the processor through your
● 21
Python 3: Programming and GUIs for Electronic Engineers operating system. So if that's what a C compiler does, why are we going to need Python? Basically Python is a whole lot easier to use than C. Python is an interpreted language. The Python interpreter has to be installed on the computer for the operating system you are using. Python is a cross platform language. Python takes your script and compiles it into something called byte code that runs on a virtual machine on your operating system; this compilation occurs when you start a program. This is slower than a compiled C program but what is too slow depends on what you’re doing. Python is fast enough for the applications in this book. In Python the above programs would be: print (“It’s a lot easier in Python!”)
To any readers who are new to Python 3 but have some experience of Python2 you will notice some things have changed, for example the "print 'Hello World'" statement is now a function and is written as "print('Hello World')". There are other differences.
2.2 • Installing Python 3 This book deals with Python 3. Although Python 2 is still widely used, if you are new to Python you should start with Python 3. First check to see if Python 3 is installed by typing "python3" at the command line. user@debian:~$ python3 Python 3.4.2 (default, Oct
8 2014, 13:14:40)
[GCC 4.9.1] on linux Type “help”, “copyright”, “credits” or “license” for more information. >>>
This puts you into an interactive session with Python which we will deal with later, so to escape from this use "Ctrl d", that is to press the Control key and d simultaneously. Make sure you have Python 3. If not it can be easily installed. While connected to the Internet, type this at the command line: user@debian:~$ sudo apt-get install python3
One of the really good features of a Debian based Linux OS is the package management that makes installation and removal of software very easy. Not all Linux distributions are this easy, notably Slackware. Although my favourite, it can be a lot more work as you have to manually find and install any other software that the software you are installing is dependent on. These dependencies can go to several layers.
2.3 • Start Programming Open Mousepad by pressing Alt + F2 and entering "mousepad". Alternatively use any text editor you prefer. Type in the following program (Note how lines two and three are indented - this is part of the code logic. Although the code is provided typing some of the programs will help to familiarise you with the language):
● 22
Chapter 3 • Graphic User Interfaces
Chapter 3 • Graphic User Interfaces 3.1 • Introduction As far as functionality is concerned, adequate programs can be written with just the command line as an interface. However a graphical interface is much more attractive, offering the usual widgets that most computer users are used to using: buttons, check buttons, text entry boxes, etc. Python offers a simple way of creating these interfaces for your programs. Two of the projects later in the book will plot graphs of values derived from analog quantities being measured. The last chapter will show you another way of producing GUIs for platforms that have lower resources and do not need the X-server. As mentioned before, there is no intention of trying to treat any of the topics in the utmost depth but rather to give the reader a set of useful introductory tools to build projects that work and give pointers to further learning. Presented here are some simple examples. The best way to understand them is to run and experiment with them.
3.2 • Tkinter Tkinter is the standard library that provides python bindings to Tk which is a graphical programming library. Tkinter offers a great deal of variety. I have presented a few example programs that do not by any means cover all of what is available. I would recommend running the example programs and altering them to suit you. The widgets that make up the interface have attributes with particular names, only some are in the example programs. Firstly you should test to see if you have tkinter installed. Start a Python 3 interactive session and try "import tkinter". If it is not installed you will get the result below: user@debian:~$ python3 Python 3.4.2 (default, Oct
8 2014, 13:14:40)
[GCC 4.9.1] on linux Type "help", "copyright", "credits" or "license" for more information. >>> import tkinter Traceback (most recent call last): File "/usr/lib/python3.4/tkinter/__init__.py", line 39, in <module> import _tkinter ImportError: No module named ‘_tkinter’
During handling of the above exception, another exception occurred: Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/usr/lib/python3.4/tkinter/__init__.py", line 41, in <module> raise ImportError(str(msg) + ‘, please install the python3-tk package’) ImportError: No module named ‘_tkinter’, please install the python3-tk package >>>
To install tkinter, do the following at the command line while connected to the Internet. user@debian:~$ sudo apt-get install python3-tk
● 59
Python 3: Programming and GUIs for Electronic Engineers We will start by creating the simplest graphical program that creates an empty window and then does nothing other than resize, minimize, maximize and close. These three lines of code actually do a lot of work. Program 3-1 Empty window #prog_03_01 import tkinter root = tkinter.Tk() root.mainloop()
# Tk() is the top level class. # Mainloop is the loop that responds to events like button clicks.
Although there are only three lines in this program, there are three new things to point out. First we import "tkinter", the package we need to create the GUIs. Second the line "root = tkinter.Tk()" is an example of creating an instance of a class, something we will go into in more detail in the chapter on Object Oriented Programming (OOP). Here root is the name that is given, in this case to an instance of the class Tk, the name is your choice. As a real world analogy you are an instance of the class of humans and have a name. Third the line "root.mainloop()" means that having created the instance root, we call the "mainloop()" function or method that runs the event driven loop that controls our interface. Pressing a button would be an event and cause something to be done by calling some function in your program. We will now build some very short example GUIs to see how to add widgets, call functions to react to events, and control the layout and appearance. Different widgets will be introduced as we progress.
3.2.1 • Click to Start Programs Up until now our programs have been started using the command line instruction "python3 prog_xxx.py". We can make the script executable by using the command line instruction "chmod 755 prog_xxx.py". This will change the file permissions (see Chapter 1.8 on page 18). In order for the script to run, the first line is added "#!/usr/bin/ env python3". Although this looks like a comment, it starts with "!" then the path to the Python interpreter. When put on the first line, Linux knows to call "python3" to run the script. We can start the program either from the command line with "./prog_03_02. py" the "./" (means look for the file in the current directory) or you can double click on the file in the File manager. Another way is to copy the executable script to the Desktop from where it can run from by double clicking on it. I would recommend starting from the command line when debugging programs.
● 60
Python 3: Programming and GUIs for Electronic Engineers
3.6.1 • Scale Widget and Label The scale widget can be used as in input and output interface. It is particularly useful for control applications because it mimics a traditional analogue control device. This next program, Program 3-7 also introduces the Label widget, which presents read only text to the user. In the program, a Scale called ‘scale_in’ is created with options to set it as horizontal, 500 pixels long, a resolution of 0.1 and to call the ‘drive_output()’ function when it is moved. Not apparent by inspecting the code, the scale passes a value of its position to the function being called, so we have the argument ‘scale_value’ in the function. It can be any legal name. This variable is a string holding the floating value of the position. The value of the scale position is converted to a floating point assigned to the local variable r. The text for the label has been formatted to give five decimal places in the circumference output and a minimum width including the decimal point of nine characters. The radius output has one decimal place and a width of five characters. Both have been padded with leading zeros. This gives a smoother digital display without the position jumping. The "\t" inserts a tab space in the string. To get the value of pi, the math library has been imported. Program 3-7 Label widget #!/usr/bin/env python3 #prog_03_07.py import tkinter import math def drive_output(scale_value): r = float(scale_value) c = 2 * math.pi * r lbl_out.configure(text = ‘Radius {0:05.1f} \t-\t Circumference {1:09.5f}’.format(r, c)) root = tkinter.Tk() root.title(‘Scale Input’) scale_in = tkinter.Scale(orient = ‘horizontal’, length = 500, resolution = 0.1, command = drive_output) lbl_out = tkinter.Label() lbl_out.grid(row = 0, column = 0, pady = 10) scale_in.grid(row = 1,column = 0, pady = 10, padx = 10) root.mainloop()
Program 3-8 on page 67 uses another Scale to display the output value. Here the scale’s set() method is used to determine the position of the slider. Also added are "tickinterval" markers and an option called label that is part of the Scale object. A "commented out" line is the alternative method of obtaining the scale position directly as a float. To try this move the comment prefix # to the other line. The definition of the ‘scale_out’ does not include a call back as it is being used as an output widget not an input. The lines that create the instances of Scale, scale_in and scale_out are long lines of code and it is recommended to limit printed lines in the editor to 80 characters. You can fold back code over more lines if that part is enclosed in parenthesis which this part is.
● 66
Chapter 3 • Graphic User Interfaces Program 3-8 Scale to display ##!/usr/bin/env python3 #prog_03_08.py import tkinter import math def drive_output(scale_value): r = float(scale_value) #r = scale_in.get() # Can also be used c = 2 * math.pi * r scale_out.set(c) root = tkinter.Tk() root.title(‘Scale Input’) scale_in = tkinter.Scale(orient = ‘horizontal’, length = 500, resolution = 0.1, command = drive_output, label = ‘Radius’, tickinterval = 10) scale_out = tkinter.Scale(orient = ‘horizontal’, length = 500, resolution = 0.1, label = ‘Circumference’, tickinterval = 100, to = 700) scale_out.grid(row = 0,column = 0, pady = 10, padx = 10) scale_in.grid(row = 1,column = 0, pady = 10, padx = 10) root.mainloop()
3.6.2 • Entry If you want to type in data directly, the Entry widget can be used to accept a single line of text. Program 3-9 below takes a text input of a numerical value. When the button is clicked, the function Calculate() converts the string in the Entry to a float and squares it putting the answer in the label. There is no protection here for non numerical data being entered. You can have your code put a string in the entry box by using the method insert(index, s) where s is the string to insert in the entry box and it gets inserted before the position index. Program 3-9 Text input #!/usr/bin/env python3 #prog_03_09 import tkinter def calculate(): x_string = ent_input.get() x = float(x_string) y = x*x y_string = str(y) lab_output.configure(text = y_string) root = tkinter.Tk() root.title(‘Entry’) ent_input = tkinter.Entry(root) ent_input.insert(0,’0.0’) lab_output = tkinter.Label(root,text = ‘0.0’)
● 67
Chapter 4 • Object Oriented Programming
Chapter 4 • Object Oriented Programming 4.1 • Introduction So far our programs have been what are called procedural programs. Object orientation is a different way of writing programs. It is about creating software models of real world items as separate objects; these objects contain the data and the functions that are associated with objects. Please be aware that the treatment of this topic is just enough to achieve the aim of producing working interfaces. We will introduce new concepts, the class, and inheritance. The objects we are talking about here are individual instances of a class. You are an instance of the class human. You have a name which can be used to distinguish you from other instances of the class human. The class of human has the attribute height if your name is jsmith. Your height might be “jsmith.height = 1.6”.
4.2 • Class The block of code that is the class is like a factory that produces instances of that class. These instances encapsulate data and methods, methods being the term used for functions in classes. This is a convenient way of programming as programs get more complicated. Let us consider for an initial example the electronic circuit of the voltage comparator. If the input voltage is above a certain level, we will call the setpoint, then the output is on, otherwise it is off. In python we could create a class to represent voltage comparators like these, as in Program 4-1. This program has a class named “Comparator”. The class is used to create two instances of voltage comparators that have different setpoints. To create a class, we start with the word “class” then give it a name, in this case Comparator. It is the convention to start class names with a capital letter. Indented below this first line is the rest of the class. Our class here has two methods. Moving down to the line “comp1=....”, after the end of the class code, this line creates an instance of a voltage comparator I have called comp1. When creating an instance of a class, the method __init__ () of that class is called automatically, if it exists. The word init has two underscores on both sides. Now look at this method: there are two arguments, the first is “self” meaning that the function belongs to this instance. Program 4-1 Voltage comparators #!/usr/bin/env python3 # prog_04_01a.py class Comparator: def __init__(self, setpoint): self.switch_level = setpoint self.output = False def switching(self, voltage): if voltage > self.switch_level: self.output = True else: self.output = False
# Method runs whenever a new instance is created
# End of class code
● 73
Python 3: Programming and GUIs for Electronic Engineers comp1 = Comparator(5) comp2 = Comparator(6)
# Creates instance comp1, calls method __init__ # Creates instance comp2, calls method __init__
print(‘comp1 output is {}’.format(comp1.output)) print(‘comp2 output is {}’.format(comp2.output))
The second argument which I have named setpoint is the argument passed to the __ init__() method whenever a new instance is created. Here the value is 5. The next line sets the variable switch_level to 5 which will be the setpoint for the comparator comp1. Again the self means that this variable belongs to the instance being created. The last line of __init__() sets the variable output to off or False. Moving on to the line “comp2=.......”, this line creates another instance of Comparator but this time when __init__() runs, the setpoint is set to the value 6. Figure 4-1 is a pictorial overview of this instance creation. Looking at the last two line of the program, these lines print out the states of the outputs. The function switching(self, voltage) has not been called yet.
Figure 4-1 Pictorial overview of instance creation Running Program 4-1 at the command line gives the following. user@debian:~$ ./prog_04_01a.py comp1 output is False comp2 output is False user@debian:~$
Now let’s apply an input voltage of 5.5 to our two instances of the Comparator class. This is the purpose of the switching(self, voltage) method. To call the switching() method of comp1 with an input value of 5.5 you use “comp1. switching(5.5)” and for comp2, “comp2.switching(5.5). Program 4-2 on page 75 shows these program lines added and two lines to print out the new output states.
● 74
Chapter 5 • The Arduino Uno as a Slave
Chapter 5 • The Arduino Uno as a Slave 5.1 • Introduction The first edition of this book used the Future Technologies Development International (FTDI) UM245 USB interface to read and write single bytes from and to the project hardware. The approach of this book is to use the Arduino Uno as a slave. No code writing is required for the Uno as an Arduino Uno universal sketch is provided. This makes the Arduino respond to programs written in Python on the PC which is the master. The sketch is provided as a hex file that AVRDUDE can load into your Arduinos. A Python tool is provided in the programs. This chapter shows how to program and configure Arduinos and will give an overview of how to use the communication functions in Python to command the slaves. At the end of the chapter, more details on the protocol will be given, although useful information on fault finding can be read later.
5.2 • The Master Slave Design The programs written in Python have GUIs that the user can use to monitor and control real electronic hardware. The Arduino Uno provides a hardware interface of digital and analogue inputs and outputs (IO) that can be connected to electronic circuits. The Arduino is the slave and the PC running the Python program is the master. For reliable communications between the Python program and the IO, a serial communications protocol is provided that has a number of functions that transmit data in frames back and forth. Each frame has an error detecting check sum.
5.2.1 • USB Serial Before your Python programs can talk to the slave, you will need to install the Python serial library. This is very easy from the command line, just type the following: user@debian:~$sudo apt-get install python3-serial
This is needed to use the USB serial interface. However you will need to add yourself to the "dialout" group otherwise you will not have permission to open the ports "/dev/ ttyACM*". To add yourself to the "dialout" group, type the following at the command line: user@debian:~$sudo usermod -a -G dialout
user
Note use your username where I have put "user".
5.2.2 • Uploading the Arduino Program The Arduino program (sketch) has already been written using the Arduino C/C++ functions and has been compiled to a ".hex" file that you can upload to your Arduino Uno. This is done using AVRDUDE, an open source project written by Brian S Dean. AVRDUDE is a command line utility that requires several command line arguments. A Python GUI tool is presented here to make it easy. Figure 5-1 on page 82 shows the user interface. Installing AVRDUDE is easily done by typing this at the command line:
● 81
Python 3: Programming and GUIs for Electronic Engineers
user@debian:~$ sudo apt-get install avrdude
When this has completed you can then use the Python GUI tool.
Figure 5-1 Slave programming tool To use the programming tool, proceed as follows. •
Run the program "arduino_slave_programming_tool_V1.0.py" in the programs for this chapter in the zip file. Start the program without your Arduino USB lead unplugged. Ensure that you run the program from this directory, see the explanation below.
•
Click the "Scan" button and note any ports displayed in the window.
•
Plug in the Arduino to be programmed and click on the "Scan" button again. The port used by the Arduino should appear.
•
Select this port and then click on the "Program" button. The output window should display as shown above.
The programming tool has to access the hex file that it knows by the name of slave.hex. It is a copy of the hex file for the current sketch, Arduino_Slave_17_03_26a.ino.standard. hex. If you move the programming tool to a different directory you must move the file slave.hex as well.
5.2.3 • Configuring the Arduino with its Address and Project ID You might have more than one project in existence, so to prevent accidentally running the wrong software with a hardware project the Arduino is given a project identity number. The master program on the PC will only command Arduinos with their project ID. Also you might have more than one Arduino in your project so the slaves are given an address within this project. These two numbers are written to the Electrically Erasable Programmable Read-Only Memory (EEPROM) in the Arduino with another tool provided in the zip file. The address and the project numbers are retained in the EEPROM when the Arduino is powered off so they only have to be entered once for each project.
● 82
Chapter 5 • The Arduino Uno as a Slave
Figure 5-9 Arrangement to write to digital outputs Program 5-1 Use check buttons to select state of output #!/usr/bin/env python3 #prog_05_01.py import tkinter import uno_slave class GuiDigOuts(): def __init__(self, master): self.uno1 = uno_slave.Uno(address = 1,project = 1, config_digital = [2,3,4,5,6,7,8,9]) master.title(‘Write Digital Outputs’) self.chkvar_2= tkinter.IntVar() # Control variables self.chkvar_3= tkinter.IntVar() self.chkvar_4= tkinter.IntVar() self.chkvar_5= tkinter.IntVar() self.chkvar_6= tkinter.IntVar() self.chkvar_7= tkinter.IntVar() self.chkvar_8= tkinter.IntVar() self.chkvar_9= tkinter.IntVar() self.chkbtn_2 = tkinter.Checkbutton(master,text = ‘Pin 2’, variable = self.chkvar_2) self.chkbtn_3 = tkinter.Checkbutton(master,text = ‘Pin 3’, variable = self.chkvar_3) self.chkbtn_4 = tkinter.Checkbutton(master,text = ‘Pin 4’, variable = self.chkvar_4) self.chkbtn_5 = tkinter.Checkbutton(master,text = ‘Pin 5’, variable = self.chkvar_5) self.chkbtn_6 = tkinter.Checkbutton(master,text = ‘Pin 6’, variable = self.chkvar_6) self.chkbtn_7 = tkinter.Checkbutton(master,text = ‘Pin 7’, variable = self.chkvar_7) self.chkbtn_8 = tkinter.Checkbutton(master,text = ‘Pin 8’, variable = self.chkvar_8) self.chkbtn_9 = tkinter.Checkbutton(master,text = ‘Pin 9’, variable = self.chkvar_9) self.btn_write = tkinter.Button(master,text = ‘Write’, command = self.dig_write) self.chkbtn_2.grid(row = 0, column = 0, pady = 5, padx = self.chkbtn_3.grid(row = 0, column = 1, pady = 5, padx = self.chkbtn_4.grid(row = 0, column = 2, pady = 5, padx = self.chkbtn_5.grid(row = 0, column = 3, pady = 5, padx = self.chkbtn_6.grid(row = 0, column = 4, pady = 5, padx = self.chkbtn_7.grid(row = 0, column = 5, pady = 5, padx = self.chkbtn_8.grid(row = 0, column = 6, pady = 5, padx = self.chkbtn_9.grid(row = 0, column = 7, pady = 5, padx = self.btn_write.grid(row = 1, column = 0, columnspan = 8)
5) 5) 5) 5) 5) 5) 5) 5)
● 89
Python 3: Programming and GUIs for Electronic Engineers def dig_write(self): self.uno1.write_digital_dict[2] self.uno1.write_digital_dict[3] self.uno1.write_digital_dict[4] self.uno1.write_digital_dict[5] self.uno1.write_digital_dict[6] self.uno1.write_digital_dict[7] self.uno1.write_digital_dict[8] self.uno1.write_digital_dict[9] self.uno1.write_digital()
= = = = = = = =
self.chkvar_2.get() self.chkvar_3.get() self.chkvar_4.get() self.chkvar_5.get() self.chkvar_6.get() self.chkvar_7.get() self.chkvar_8.get() self.chkvar_9.get()
tk = tkinter.Tk() front_end = GuiDigOuts(tk) tk.mainloop()
Program 5-2 is for the same circuit. It drives one LED on at a time so that the position of the lit LED moves backwards and forwards. The speed of the change of position is controlled by a scale. This program introduces a useful technique for calling a method at intervals. Top level windows, in this case our master has a function called self.master. after() that calls a function after a time interval. Here we are using it at the end of the method drive() to call drive() again. The time interval is determined by the position of the scale. Unlike using time.sleep(), the method after() does not block or in other words stall the flow of the program. A point to remember is that sleep() has the time interval expressed in seconds using a float, and the after() method has the time interval expressed in milliseconds using an integer. Program 5-2 Call a method at intervals #!/usr/bin/env python3 #prog_05_02.py import tkinter import uno_slave import time class GuiDigOuts(): def __init__(self, master): self.master = master self.uno1 = uno_slave.Uno(address = 1, project = 1, config_digital = [2,3,4,5,6,7,8,9]) self.master.title(‘Write Digital Outputs - Timed Sequence’) self.rate_scale = tkinter.Scale(master, orient = ‘horizontal’, length = 500, from_ = 1, to = 100, tickinterval = 10,label = ‘Step Interval (ms)’) self.uno1.write_digital_dict = {2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0,} self.step = 2 self.direction = 1 self.rate_scale.grid(row = 0, column = 0, padx = 10,pady = 10) self.drive() def drive(self): self.uno1.write_digital_dict[self.step] = 0 if self.step == 9: self.direction = -1 if self.step == 2: self.direction = 1 self.step = self.step + self.direction self.uno1.write_digital_dict[self.step] = 1 self.uno1.write_digital() delay = self.rate_scale.get() delay = int(delay)
● 90
Chapter 6 • Further Examples of GUIs
Chapter 6 • Further Examples of GUIs 6.1 • Introduction This chapter moves on to more advanced GUIs and introduces further aspect of Python and Tkinter. The GUIs will be all about interfacing with the Arduino slave. You must read Chapter 5 on page 81 before trying the projects in this chapter in order to understand how the Arduino is used here. The programs presented will be object oriented. The programs will be getting longer and more complicated. This is where object oriented programming starts to make things more manageable. If you have trouble understanding parts of the code, try experimenting by changing one part at a time; this can be great help towards understanding. Also some more Python features will be introduced.
6.2 • A Graph Plotting Class As shown in Chapter 3 on page 61, there are a lot of standard widgets available but you have to write code to make them do what you want. Often something similar crops up in more than one program that would result in duplicating code and wasting time. One such example is to plot a graph of the analogue inputs against time. The following program is a class that can be used to create instances of an object that will do this and require very little code to include it in other projects. The class that is called Graph and it will be in a module or separate file. Figure 6-1 shows a GUI that will be Program 6-2 on page 110, the custom widget with the canvas and two buttons Reset and Flyback can be seen with its raised border, is an instance of the class Graph. This widget can be placed in any GUI you write, simply by importing the module plotter and creating an instance of Graph and calling its methods. Program 6-1 below is plotter.py. The class plots up to six analogue inputs simultaneously against time. Measurements on the graph can be taken by using the mouse to point to a position on the graph and left clicking. The Flyback button causes the graph to restart plotting from the left end without deleting anything. The Reset button also restarts from the left end but also clears the canvas. As well as the time saving, this approach gives the benefit of dependability that the code can be relied upon and only needs to be imported and used.
Figure 6-1 GUI created by Program 6-2
● 101
Python 3: Programming and GUIs for Electronic Engineers
6.2.1 • Passing Arguments to a Function without Specifying How Many (*args) Up until now we have been specifying the arguments that are being passed to a function, see paragraph 2.7 on page 42. Suppose that we want to pass different numbers of arguments depending on the application. In Program 6-1 on page 103, notice the function definition "def plot (self, *args)". Here we can pass up to 6 analogue input values to be plotted. In the function, args is a tuple that contains all the arguments passed from the calling function. The word args is not mandatory, it is just the convention. What is important is that it is prefixed with the single *. Here is an example you can try in an interactive session. >>> def fun(*args): ... print(args) ... >>> fun('first_argument', 100.05, 99, 'last_argument') ('first_argument', 100.05, 99, 'last_argument') >>>
The function fun() is called with four arguments: the first a string, the second a float, the third an integer and finally another string. In the function definition *args is used. The function prints args; you can see that this is a tuple. If we use the built in method enumerate() we can obtain index numbers and values for the arguments that have been passed regardless of how many there are. The function enumerate() yields two values: one being an enumeration of the position of the argument and the other being its value. >>> def fun(*args): ... print(args) ... for i, v in enumerate(args): ...
print('argument {0:d} = {1:s}'.format(i,str(v)))
... >>> fun('first_argument', 100.05, 99, 'last_argument') ('first_argument', 100.05, 99, 'last_argument') argument 0 = first_argument argument 1 = 100.05 argument 2 = 99 argument 3 = last_argument >>>
In the method plot() (Program 6-1), the arguments for the analogue inputs are enumerated to be the variable channel and y the value. The variable palette is a list of different colours for each analogue input.
6.2.2 • Passing Arguments to a Function with Keyword Arguments If you look at the __init__() method for the class two of the arguments are keyword arguments scale_x and scale_y. They are after the two positional arguments height and width. Keyword arguments have default values that they take if they are not passed by the calling method these are the values after the equal signs. If values are passed by the
● 102
Chapter 7 • Bit Map Graphics
Chapter 7 • Bit Map Graphics 7.1 • Introduction This chapter is mainly about the anatomy of bit map images and how to operate on them. In the last chapter the canvas widget was used to draw on when producing GUIs in Tkinter. What if you want to plot a graph and save it as an image file? Python does provide a method for saving canvases as postscript files, but what we will do in this chapter is create and modify bitmap images that can be saved and read by any image viewer. Bit map images are very easy to manipulate as they are an array of pixels like points on a piece of graph paper.
7.2 • The Bit Map File The bit map images we will be dealing with will have each pixel colour defined by three bytes, one for each primary colour. As an illustration Figure 7-1 represents an image 8 by 6 pixels. There is a square for each pixel and a byte for each of the three colours. The image is white except for a red line across the top and the three pixels starting from the bottom left hand corner are red, green and blue.
Figure 7-1 Image of 8 pixels by 6 pixels Not shown on Figure 7-1 the bit map file starts with a string of 54 bytes which is the header. This contains information about the image - we needn't worry about the content of the header for now. Following the header is the actual image content, starting from the bottom left hand corner of the image, the next three bytes are the blue, green, and then red values of the first pixel. This continues for each pixel in the bottom row. After the bottom row is the left most pixel of the next row up and then so on for the rest of the image. In order to view and modify images I recommend KolourPaint. This can be installed easily on Debian by typing the following at the command line: user@debian:~$ sudo apt-get install kolourpaint4
● 119
Python 3: Programming and GUIs for Electronic Engineers The image depicted by Figure 7-1 on page 119 is in the zip file, its name is fig1image. bmp. To view it you will need maximum zoom. Figure 7-2 shows how the bytes that define the image are sequenced in the file. The first four pixels and the last two from Figure 7-1. Note that the order of the bytes for each pixel is blue, green and then red.
Figure 7-2 Byte sequence in the file The object of the exercise is to write an image file with our own program. This will be referred to as the output file. Although we don't need to worry about the content of the header, we do need a header for any file we create. Fortunately the header does not depend on the image content but only on the geometry of the image and colour depth. The easiest way to write the header for your file is to copy the header from another image of the same size and colour depth. We will look at the makeup of the header later in the chapter. We will take an existing bit map file, our template image, and read it. The header will be re-used as the header for the output file. The template file can give the program a head start by having a background such the grid lines for a graph or the face of an instrument to draw on top of. Looking again at Figure 7-1 on page 119, each square is a pixel and needs three bytes to set its colour. We can create a list with three members each being an integer from 0 to 255. This would mean the third pixel from the left hand corner would be [255, 0, 0]. If we have a separate list for each pixel we need some way of identifying these lists in terms of where they are on the image. One python object that is ideal for this is the dictionary. Dictionaries were covered in paragraph 2.6.5 on page 40; the members of a dictionary are indexed by keys. See below on how any pixel from Figure 7-1 could be held in a dictionary. The keys that we are going to use to index the lists that define the pixel colours are tuples with two integer members. These integers are the horizontal and vertical offsets from the bottom left hand corner of the image. image_content_dictionary = {(7, 3): [255, 255, 255], (1, 3): [255, 255, 255], (3, 0): [255, 255, 255],................}
● 120
Chapter 8 • A Project to Monitor Your Electricity Consumption
Chapter 8 • A Project to Monitor Your Electricity Consumption 8.1 • Introduction This chapter is about a larger project that will monitor the current in a mains electricity supply. The first thing to point out that there is no direct connection to the mains wiring required. A non-invasive technique is used involving a coil of wire placed near to the supply cables. The probe will measure the current flowing and transmit the value to the receiver that will be connected to the Arduino slave. A graph of current against time will be plotted. The next chapter gives details of how to use a web browser as a GUI that can be viewed on a mobile phone or other computer. This will allow you to view the output when away from home. The reason for the radio link is that very often the mains incomer to a building is not at a convenient place for a computer. The remote sensing head driven by the coil comprises a filter, amplifier, and precision half wave rectifier, a PIC microcontroller and a low power radio transmitter. The PIC microcontroller is a 12F1822 and it can be programmed using a tool written in Python that uses the Arduino slave. The code for the PIC is provided. The transmitter and receiver are readily available from Farnell Components who have international distribution.
Figure 8-1 Block diagram of mains supply monitor This chapter will also introduce some new topics by making use of the desktop application used for this project.
8.2 • The Probe and Remote Sensing Head The probe that was used in the prototype was a coil of insulated wire, air cored with 5 turns and a diameter of approximately 60mm, as shown in Figure 8-2 on page 132. The pickup coil is connected to the sensing head by a length of coaxial cable. A twisted pair could be used. This is not the usual way of measuring current in a supply cable. Normally a current transformer with a magnetic core that one of the supply cables runs through is used. The core has a winding on it connected to a low impedance circuit. This has the disadvantage that either the current transformer must be fitted to the cable before it is connected or a split core transformer must be used. Either option is not suitable for a home construction project as even if you use a split core you probably won't have room to fit it on a domestic installation. The method described here is completely non-invasive. The way that this sensing coil works is that some of the magnetic field from the two supply cables links with the coil. As one of the cables is closer to the coil than the other, there will be a resultant field that induces a voltage in the coil. Now this is where this method is flawed. The instantaneous magnetic flux from the supply current is proportional to the instantaneous current in the supply cable but the voltage induced in the coil is proportional to the rate of change of the magnetic flux. If we are only considering one
● 131
Python 3: Programming and GUIs for Electronic Engineers frequency (50Hz in the UK), the maximum rate of change of flux is proportional to the maximum instantaneous current, only phase shifted by 90 degrees. However there are harmonics present in the supply current, if you compare the maximum rate of change of a 150Hz sine wave with the maximum rate of change of a 50Hz sine wave of equal amplitude it is three times greater, five times greater for the fifth harmonic. To overcome this problem a low pass filter is included in the sensing head to reduce the effect of any harmonic content in the supply. It must be pointed out that this will not give results accurate enough for metering purposes. Please note, on the photograph the supply cables are the two running above the coil. In the UK these are referred to as the meter tails.
Figure 8-2 Prototype probe Figure 8-3 shows the circuit diagram for part of the sensing head. There are three LF411 operational amplifiers used. The first one 'U1' is used as a two pole Sallen and Key low pass Butterworth filter with a corner frequency around 50Hz. If your local supply is 60Hz this won't matter. The second stage 'U2' is a non-inverting preamplifier with a gain of 101. Another low pass filter is added using 'R3' and 'C3'. The last stage 'U3' is a precision half wave rectifier. The gain of the last stage is set by selecting a value for 'R7'. The gain is the ratio of 'R7' to 'R6'. In my prototype, a value of 80kΩ was used giving a gain of 80. You might find it more convenient to use a variable resistor here. Its value will be determined on test. Note carefully that the first two opamps U1 and U2 are connected as non inverting amplifiers and the third U3 is connected as an inverting amplifier; in other words get pins 2 and 3 the right way round. Capacitor C4 should be a non-polarized type such as metalised polyester.
Figure 8-3 Circuit diagram for the sensing head
● 132
Python 3: Programming and GUIs for Electronic Engineers self.calibration_dict = {‘raw_offset’:0, ‘raw_to_current’:1, ‘fullscale_current’:100} self.circ_buffer = collections.deque([], maxlen=50) #add menus menubar = tk.Menu(self.master) master.config(menu=menubar) commissioning_menu = tk.Menu(menubar, tearoff = 0) menubar.add_cascade(label = ‘Commissioning’, menu = commissioning_menu) commissioning_menu.add_command(label = ‘Calibration’, command = self.calibration) commissioning_menu.add_command(label = ‘Future Use’) commissioning_menu.add_command(label = ‘Future Use’) commissioning_menu.add_separator() commissioning_menu.add_command(label = ‘Future Use’) screen_capture_menu = tk.Menu(menubar, tearoff = 0) menubar.add_cascade(label = ‘Screen Capture’, menu = screen_capture_menu) screen_capture_menu.add_command(label = ‘Capture’, command = self.capture) self.num_samples = self.X self.graph1 = plotter.Graph(master,self.X, self.Y, scale_x = self.num_samples, scale_y = self.calibration_dict[‘fullscale_current’]) self.graph1.frm.grid(row = 0, column = 0, padx = 10, pady =10,columnspan = 3) self.reading() def reading(self): self.uno1.read_analog(1) raw = self.uno1.read_analog_dict[0] i = (raw-self.calibration_dict[‘raw_offset’]) * self.calibration_dict[‘raw_to_current’] y = int(i*self.Y/self.calibration_dict[‘fullscale_current’]) self.circ_buffer.append([raw,i,y]) if self.x%self.X == 0: self.graph1.flyback() self.graph1.plot(y) self.x = self.x + 1 self.master.after(10000, self.reading) def calibration(self): self.cal_window = tk.Toplevel() self.cal_window.title(‘Calibration’) self.cal_output = tk.Text(self.cal_window, width = 30, height = 20, relief = ‘ridge’) self.btn_save_cal = tk.Button(self.cal_window, text = ‘Save Calibration’, command = self.save_calibration) self.lbl_raw = tk.Label(self.cal_window, text = ‘Raw/Current/Screen Position’) self.lbl_offset = tk.Label(self.cal_window, text = ‘raw_offset’) self.strvar_offset = tk.StringVar() self.strvar_offset.set(self.calibration_dict[‘raw_offset’]) self.ent_offset = tk.Entry(self.cal_window, textvariable = self.strvar_offset, width = 10) self.lbl_raw_to_current = tk.Label(self.cal_window, text = ‘raw_to_current’) self.strvar_raw_to_current = tk.StringVar() self.strvar_raw_to_current.set(self.calibration_dict[‘raw_to_current’]) self.ent_raw_to_current = tk.Entry(self.cal_window, textvariable = self.strvar_raw_to_current, width = 10) self.lbl_fullscale_current = tk.Label(self.cal_window, text = ‘fullscale_current’) self.strvar_fullscale_current = tk.StringVar() self.strvar_fullscale_current.set(self.calibration_dict[‘fullscale_current’]) self.ent_fullscale_current = tk.Entry(self.cal_window, textvariable = self.strvar_fullscale_current, width = 10) self.cal_window.grid() self.lbl_raw.grid(row = 0, column = 0) self.cal_output.grid(row = 1, column = 0, rowspan = 7) self.lbl_offset.grid(row = 1, column = 1, sticky = ‘S’) self.ent_offset.grid(row = 2, column = 1, sticky = ‘N’) self.lbl_raw_to_current.grid(row = 3, column = 1, sticky = tk.S) self.ent_raw_to_current.grid(row = 4, column = 1, sticky = tk.N) self.lbl_fullscale_current.grid(row = 5, column = 1, sticky = tk.S)
● 138
Chapter 8 • A Project to Monitor Your Electricity Consumption self.ent_fullscale_current.grid(row = 6, column = 1, sticky = tk.N) self.btn_save_cal.grid(row = 7, column = 1, padx = 5, pady = 5, sticky = tk.S) self.calibration_update() def calibration_update(self): try: self.cal_output.delete(1.0, tk.END) for data in self.circ_buffer: self.cal_output.insert(tk.END,’raw = {0:d} i = {1:0.2f}A y = {2:d}\n’.format(data[0], data[1], data[2])) self.cal_output.see(tk.END) except: self.master.after_cancel(self.after_handle) self.after_handle =
self.master.after(10000, self.calibration_update)
def save_calibration(self): self.calibration_dict[‘raw_offset’] = float(self.strvar_offset.get()) self.calibration_dict[‘raw_to_current’] = float(self.strvar_raw_to_current.get()) self.calibration_dict[‘fullscale_current’]= float(self.strvar_fullscale_current.get()) try: calibration_file = open(‘current_monitor_cal.dat’,’bw’) pickle.dump(self.calibration_dict, calibration_file) calibration_file.close() except: self.cal_output.insert(END,’\vFailed to open file to save calibration\n’) self.cal_output.see(END) def capture(self): file_name = tk.filedialog.asksaveasfilename(filetypes = [(‘Bit Map’,’*.bmp’)],title = ‘Choose or Enter File Name’) if file_name: segments = [] tags = self.graph1.can.find_all() # find all objects on the canvas for tag in tags: # loop through objects if self.graph1.can.type(tag) == ‘line’: # filter out the lines segments.append(self.graph1.can.coords(tag)) # make a list of coordinate lists draw1 = bmp_draw.BmpDraw(‘current_monitor_screen_cap_template.bmpt’) if draw1.flag == True: for x in range (0,self.X, 90): draw1.line(x, 0, x, self.Y, [0,255,0]) for y in range (0, self.Y, 50 ): draw1.line(0, y, self.X, y, [0,255,0]) for segment in segments: draw1.line(segment[0], self.Y - segment[1], segment[2], self.Y - segment[3], [0,0,255]) draw1.write_file(file_name) tk.messagebox.showinfo(‘Capture’, ‘Capture Saved’) else: tk.messagebox.showinfo(“Capture”, ‘Capture Cancelled’) tkin = tk.Tk() front_end = CurrentLogger(tkin) tkin.mainloop()
● 139
Chapter 9 • Web Browser Interfaces
Chapter 9 • Web Browser Interfaces 9.1 • Introduction Desktop GUI applications are satisfactory if you only want to interface with some local hardware. It is however often more convenient to use a mobile device such as a tablet computer or mobile phone. This allows you to remotely access your hardware devices even when away from home. Security and safety have to be considered when remotely controlling real electrical and electromechanical devices. This chapter will only cover enough to give you a practical GUI for you own projects; it is certainly not a reference for web design. This chapter is being presented as following on from the current monitoring project started in the last chapter. You can easily apply the information in this chapter to other projects of your own invention that might want to monitor similar analogue or digital inputs to the Arduino slave.
9.2 • HTML 5 To generate the graphic interface, we are going to use HTML5 to display the graph from the current monitoring project from the last chapter. HTML stands for Hypertext Markup Language. It defines a web page in elements and these are represented by tags. Only the most basic coverage of this topic will be given here. We only need to generate and display an image similar to the Python canvas widget in the desktop program. Although HTML 5 has a canvas object, we will not be using it Instead we will be using Scaleable Vector Graphics (SVG). The code that we need to write here is remarkably simple. Program 9-1 Index.html <!--prog_09_01 is actually file index.html in directory ~/programs/chap09--> <!DOCTYPE html> <html> <body> <h3>Lines Using Scaleable Vector Graphics</h3> <svg height=”500” width=”720”> <line x1=”360” y1=”0” x2=”360” y2=”500” style=”stroke:rgb(0,255,0);stroke-width:2” /><!-- vertical line --> <line x1=”0” y1=”250” x2=”720” y2=”250” style=”stroke:rgb(0,255,0);stroke-width:2” /><!-- horizontal line --> <line x1=”0” y1=”0” x2=”720” y2=”500” style=”stroke:rgb(255,0,0);stroke-width:2” /><!-- diagonal --> </svg> </body></html>
The above HTML script can be viewed in a web browser by typing /home/ in the URL bar and then navigating to the file that is under the programs for Chapter 9. The file name is index.html. The first line is a comment and does nothing to the web page. Comments are enclosed by the tags “<!--” and “-->”. The next declares to the browser what type of document it is. The first tag is “<html>” and it has a closing tag on the last line of “</html>”. The next pair of tags enclose the header, and then there is the SVG code enclosed in the svg tags. The opening tag defines the size of the graphic. The lines that are drawn are defined between the “<line” and “/>”. The lines are defined in a way similar to that used on the Python canvas, with x and y coordinates for each end of the line, colour defined by the red, green and blue values, and stroke-width value. Figure 9-1 shows the resulting graphic being viewed locally by using the path to the file. To view a web page on a different device to the one that the file is on you need a web server. The Apache Web Server was used in the first edition of this book and common gateway interface (CGI) scripts were used to generate interactive web pages. In this ● 145
Python 3: Programming and GUIs for Electronic Engineers edition I am using the CherryPy Web Framework which offers a much more straight forward way of creating our dynamic web pages.
Figure 9-1
9.3 • CherryPy a Python Web Framework CherryPy is a web framework for Python that gives us a web server and a framework to serve dynamic content allowing the user to interact with Python programs running on the server. To install, you need to type this at the command line: user@debian:~$ sudo apt-get install python3-cherrypy3
We will start with the content of the “index.html” shown above and serve it using CherryPy. Program 9-2 CherryPy #!/usr/bin/env python3 #prog_09_02.py import cherrypy class SvgDemo: @cherrypy.expose def index(self): return ‘’’ <!DOCTYPE html> <html> <body> <h3>Lines Using Scaleable Vector Graphics</h3> <svg height=”500” width=”720”> <line x1=”360” y1=”0” x2=”360” y2=”500” style=”stroke:rgb(0,255,0);stroke-width:2” /><!-- vertical line -->
● 146
Chapter 10 • Curses Interfaces
Chapter 10 • Curses Interfaces 10.1 • Introduction The curses interface runs on a terminal. Curses is a Python package for the ncurses library that allows you to write text based user interfaces that are independent of the terminal being used. Although the interface is character based, many of the features of a GUI can be produced. One example of this type is the Midnight Commander File manager. Unlike a basic command line program, curses allows you to print anywhere on the screen and have multiple windows in the display for entering and reading data. Some limited graphics such as bars that change their length can be used for meter indications and windows that change colour can be used as indicators. The reason that you might want to use them is that they can run without the X-windows and therefore are useful on low resource computers. Curses is already part of Python and is simpler to use than the ncurses library that you would use if programming in C. The curses package is not available for Python on Microsoft Windows. Figure 10-1 shows an example of a representation of a gauge. There are no readymade widgets but it is easy to produce your own widgets as classes that you can re-use.
Figure 10-1 Representation of a gauge
10.2 • Getting Started with Curses As an example to start with, Prog 10 - 1 prints a message to the screen in two different positions. Normally with a terminal, printing takes place at the cursor position and you can't choose an arbitrary position on the screen, but with Curses you can. Program 10-1 Print message to screen #!/usr/bin/env python3 #prog_10_01.py import curses stdscr = curses.initscr() curses.noecho() curses.cbreak() stdscr.addstr(5, 20, 'Curses can write anywhere on the screen') stdscr.addstr(7, 10, 'Curses can write anywhere on the screen') stdscr.getch() curses.echo() curses.nocbreak() curses.endwin()
Figure 10-2 on page 164 shows the output of this program. Firstly you have to import curses and then call the function curses.initscr(). This function initialises the library and returns window object that it is customary to call stdscr, which represents the whole screen. On stdscr, text can be written and separate windows can be placed. Here there are no windows, just the stdscr. The curses.noecho() function prevents characters appearing as you type: there are special functions to do that. The curses.cbreak() function stops the line buffering of input as happens with a normal terminal that would require you to press the return key. To get text to print to the screen, the addstr() ● 163
Python 3: Programming and GUIs for Electronic Engineers function is called. This is a method of the screen or window and can take the arguments to position the text, the string to print, and also attributes for the text, but here there are none. An important note is the order of the positional arguments. The first one is the number of lines down the screen or window, and the second one is the number of columns or character places along the screen from the left. Curses does not draw individual pixels, only characters.
Figure 10-2 Program output
To prevent the program ending before you get a chance to see it, the function stdscr. getch() waits for any key press. Before the program ends, you have to undo the changes you made to the operation of the terminal, otherwise the terminal will not behave normally after the program ends. In the program you can see the three functions called to reverse these effects. If the program fails before these functions can be called, you will have the terminal in an unusable state that will require it to be closed and another opened. There is however a convenient way of overcoming this.
10.2.1 • The Wrapper Function The wrapper function handles this problem and does away with having to manually call these functions. You have to put your code in a function, in this case it is called main() and it must have one argument called stdscr. The program starts by calling curses. wrapper() and this function must be given the name of your function because it calls it. When the wrapper function calls your function, it passes it to the stdscr object. When your function finishes, the program returns to the wrapper function and it cleans up the terminal. If your function fails for any reason, the wrapper function has a try/exception routine that handles the clean up. Program 10-2 Using the wrapper function #!/usr/bin/env python3 #prog_10_02.py import curses def main(stdscr): stdscr.addstr(5, 20, 'Curses can write anywhere on the screen') stdscr.addstr(7, 10, 'Curses can write anywhere on the screen') stdscr.getch() curses.wrapper(main)
10.2.2 • Windows on the stdscr There is more to it than just writing text anywhere on the screen. Separate windows can ● 164
Appendix A • Arduino Uno Slave Source Code
Appendix A • Arduino Uno Slave Source Code Program A-1 below is the source code for the Arduino slave it is not Python it is the Arduino dialect of C and C++. This source code gets compiled to a hex file that is the executable code that gets loaded into the Arduino to make it a slave. The hex file is in the zip file as Arduino_Slave_17_03_26a.ino.standard.hex. An explanation of this program is not given, but if you are familiar with C please refer to Figure 5-12 on page 95 that is the state diagram for this program. Program A-1 Arduino Sketch /*Arduino_Slave_17_03_26a.ino*/ #include <EEPROM.h> #define #define #define #define #define #define #define
STATE_IDLE STATE_CHECK_TELEGRAM STATE_CONFIGURE_IO STATE_DIG_READ STATE_DIG_WRITE STATE_AN_READ STATE_AN_WRITE
#define #define #define #define
STATE_FAIL_SAFE STATE_TX_IDS STATE_SET_IDS STATE_RECEIVING_BYTES
7 8 9 10
#define #define #define #define #define #define #define
FUN_CODE_CONFIG FUN_CODE_DIG_READ FUN_CODE_DIG_WRITE FUN_CODE_AN_READ FUN_CODE_AN_WRITE FUN_CODE_TX_IDS FUN_CODE_SET_IDS
2 3 4 5 6 8 9
#define BROADCAST_ADDR #define #define #define #define
ERROR_CHECK_SUM ERROR_FUN_CODE ERROR_ADDRESS ERROR_PROJECT_ID
0 1 2 3 4 5 6
255 1 2 3 4
#define SLAVE_ADDRESS_EEPROM_LOCATION 0 #define PROJECT_ID_H_EEPROM_LOCATION 1 #define PROJECT_ID_L_EEPROM_LOCATION 2 unsigned unsigned unsigned unsigned unsigned unsigned unsigned unsigned unsigned unsigned volatile unsigned unsigned
char request_telegram_ascii[64]; char request_telegram_binary[32]; char response_telegram_ascii[64]; char response_telegram_binary[32]; char telegram_index = 0; char telegram_binary_length; char slave_address; char project_id_h; char project_id_l; char state = 0; unsigned char chrbuff; char pullupmask_d; char pullupmask_b;
● 185
Appendix B • The Python Uno Slave
Appendix B • The Python Uno Slave Below is the program uno_slave.py. This is the communication program that commands the Arduino slave. Program B-1 uno_slave.py #!/usr/bin/python3 import serial import os H = 1 L = 0 FUN_CODE_DIG_READ = 3 FUN_CODE_DIG_WRITE = 4 FUN_CODE_AN_READ = 5 FUN_CODE_AN_WRITE = 6 FUN_CODE_GET_IDS = 8 #maps to FUN_CODE_TX_IDs in the arduino code FUN_CODE_CONFIG = 2 BROADCAST_ADDR = 255 DEBUG = False class Uno: '''Arduino Uno as an IO slave. The Uno must be running the Arduino_slavexxx sketch. Uses an ascii formatted communications telegram. ''' def __init__(self,address = 0, project = 0, config_digital = []): '''Initialise the Uno slave. address -- 1 - 254. project -- 0 - 65535 config_digital -- List of integers detailing the pins configured as outputs. ''' self.project = project self.address = address self.config_digital = config_digital self.port = None self.rw_available__flag = False self.ser = serial.Serial() self.ser.timeout = 0.1 self.ser.bytesize = 8 self.ser.stopbits = 2 self.ser.baudrate = 9600 self.ser.xonxoff = False self.write_digital_dict = {} self.write_analog_dict = {} self.read_digital_dict = {0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0, 10:0, 11:0, 12:0, 13:0, } self.read_analog_dict = {0:0, 1:0, 2:0, 3:0, 4:0, 5:0} def connection(self): '''This function checks for connection and digital configuration and calls connect() if necessary. Is called by read_digital(), read_analog(), write_digital(), and write_analog() to check for connection to slave. Return True or False. '''
● 193
Index
Index Symbols __init__ () 73 __init__() 137 __main__ 104 __name__ 103
A
bytesarray 54 Byte slicing 55
C Calculate() 67 calibration() 141 Calibration Program 136 Callbacks 61 Calling box() 168
Adapting Project 161
Canvas() 143
administrative rights 18
Canvas Widget 70
after() 90
capture() 142
Analogue gauge 172
C compiler 22
Analogue Input Pins 87
Checkbutton() 68
Analogue Inputs 93
Check buttons 68
Analogue Output Pins 87
CherryPy Web Framework 146
Analogue Outputs 91
Class 73
Apache Web Server 11
Class BmpDraw 128
append() 143
Colour 165
Arduino Sketch 185
Commands
Arduino Slave 134
cat 17
Arduino Uno 81
cd 16
ASCII 21, 51, 96
cp 17
assembler 21
df 17
AVRDUDE 81
less 17
B
ls 16
Bash Scripts 19 Binary 50 Binary Files 121 binary system 47 Bit and Bytes 46 Bit Map 119 bitwise logic operators 47 bitwise operators 47 Bitwise OR Operator 173 bkgd() 167 BmpDraw() 144 Button Widget 178 bytearray() 55
lsusb 17 mkdir 17 mv 17 rm 17 rmdir 17 communications functions 87 Communications Protocol 95 Comparators 73, 77 Comparison Operators 48 Compiled Python 51 Configuring the router 161 Configuring Wi-Fi 158 Control Variables 68
â&#x2014;? 219
Python 3: Programming and GUIs for Electronic Engineers contvar 68
Flyback button 101
Create window 166
format() 28
Creating file names 30
Functions 42
Creating the GUI 110
G
CurrentLogger() 143 Current monitor 137 curses 163 curses.cbreak() 163 curses.initscr() 163
Graphic User Interfaces 59 Graph Plotting 101 Grid Manager 61, 62 GUI 101, 145
curses.napms() 182
H
Custom Widgets 169
Handling pixel file 123
D Data logger 28 Data Types 24 Debian 11
Header 125 Hexadecimal 50 Hexadecimal Notation 49 HTML 5 145
Debian 8 Jessie 13
I
Decimal 50
IDLE 23, 24
Decoding Bytes 56
if else 48
Desktop Program 135
Imports 44
Dictionaries 40
index() 149
Digital Input Pins 86
Indicator lamp 170
Digital Inputs 92
Inheritance 75
Digital Output Pins 85
Instances of Slaves 85
Digital Outputs 88
Integers 24
Directory handling 14
Internet access 160
drive() 90
Internet Service Provider 160
E Empty window 60
IO Functions 87 IP address 161
Encoding Bytes 56
J
Error message 23
Junk Bytes 128
except 33
L
F
Label Widget 66, 179
File handling 14
layout behaviour 63
File Permissions 18
Linux 13, 18
find() 35
list indexing 37
find_all() 143
Lists 36
Floats 25
Logic 46
â&#x2014;? 220
Index Logical operators 46
Radio Buttons 69
Long lines 52
Radio Link 135
Long string 27, 167
Raspberry Pi 11, 157
loop conditional 49
Raspbian 11, 13
M
Redirection 18
mainloop() 114 Master Slave Design 81
Remote Sensing Head 131 row.configure() 63
method test() 114
S
micro SD 158
save_calibration() 142
Mint 11
Scaleable Vector Graphics 145
Modified Image 124
Scale to display 67
Mouse Events 173
Scale Widget 66
Mousepad 15, 22
Schmitt trigger 75
Moving graphics 45
Slave State Diagram 95
N
sleep() 151
Nested Layout 64 Newline 31 "no file" error 33 Number crunching 43
O
Split function 40 Splitting Strings 39 start_test() 114 stdscr.refresh() 165 Strings 26 Struct Module 126
Object Orientation 79
struct.pack() 126
Object Oriented Programming 73
Switching() method 75
Opening file 31
T
P
Telegram Formats 97
permission number 19
Terminal Emulator 13
Persistence of Objects 50
test() 114
PIC connections 134
Test results 114
pickle.dump() 142
Text input 67
PIC Microcontroller 133
textvariable 68
privileges 18
Threading 116
Probe Sensing Head 131
Thumbwheel Widget 174
Python 2 11, 21
Time() 44
Python 3 11, 21
time.sleep() 90
Q
Tkinter 59, 101
Quantities 141
R
Trend graph 157 try 33 Tuples 42
â&#x2014;? 221
Python 3: Programming and GUIs for Electronic Engineers
U Ubuntu 11, 13 Unicode 21 Unicode Strings 51 Uno Class 84 Uno Slave 193 USB Serial 81 utf-8 53 utf-16 53
V Variable assigned 43 Variable names 57 variables 141 VirtualBox 13 Voltage comparators 73
W Web Forms 148 Web Server 157 Widgets 61, 65 Wild Cards 17 win.doupdate() 182 win.noutrefresh() 182 win.refresh() 182 Wrapper Function 164
X X-Server 20
â&#x2014;? 222
175 × 235 SPINE: 11.4 FLAPS: 0
PYTHON 3 PROGRAMMING AND GUIS
Andrew Pratt This is the second edition of a book aimed at engineers, scientists and hobbyists who want to interface PCs with hardware projects using graphical user interfaces. Desktop and web based applications are covered.
He continued his career working in industrial controls. Currently he is an instructor teaching industrial control systems.
One project involves a PIC microcontroller with code provided that can be loaded into the PIC using the Uno. The web applications and server are all implemented in Python allowing you to access your electronic hardware over the Internet. The Raspberry Pi computer can be used as your web server. An introductory chapter is provided to get you started with using Linux. The book is written for use with Debian or variations including Mint or Ubuntu. All of the programs in the book are freely available, ready to use and experiment with by way of a download from Elektor.
PYTHON 3
PROGRAMMING AND GUIS
GUIS • Andrew Pratt
He holds a Higher National Certificate in electrical and electronic engineering and a Degree from the Open University.
Hardware interfacing is achieved using an Arduino Uno as a remote slave. A full description and source code of the communication interface is given in the book. The slave provides digital and analogue input and outputs. Multiple Unos can be included in one project with all control code written in Python and running on a PC
FOR ELECTRONIC ENGINEERS
AND
Andrew Pratt served for 25 years in the Royal Air Force as an Aircraft Technician.
The programming language used is Python 3 which is one of the most popular languages around: speed of programming being a key feature. The book has been revised and updated with emphasis on getting the user to produce practical designs with ease - a text editor is all that is required to produce Python programs.
PYTHON 3 - PROGRAMMING
FOR ELECTRONICS ENGINEERS
ISBN 978-1-907920-61-5
LEARN
www.elektor.com
DESIGN
Elektor International Media BV
Andrew Pratt LEARN
DESIGN
SHARE
SHARE
LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIG
N • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHA SIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN RN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SHARE • LEARN • DESIGN • SH