In this project, a digital voltmeter will be designed which will show the voltage reading on a desktop application. The device will read analog voltage with respect to the ground, digitize the reading and send the reading to personal computer on USB interface. The device could read four different voltages in range from 0V to 5V. For this, the device will be configured as CDC Class Device. A CDC Class device has USB communication over two types of transfers – Control transfer and Bulk transfer. The control transfer will be used for enumeration of the device. After enumeration, device will use bulk transfers for sending the voltage readings to the computer.
The project device will need a controller chip to convert analog reading to digital form and send USB data to the PC. The 8-bit USB AVR – Atmega 32u4 is used as the device controller chip in the project. The project uses AVR based Lightweight USB Framework (LUFA) as the firmware which will be responsible for implementation of the USB Protocol. The device is tested on a Linux system using Python based Pyusb and Libusb frameworks for receiving data.
Fig. 1: Prototype of Arduino based USB Digital Voltmeter
The CDC Class device driver of the LUFA firmware is used and modified to program the project. With the use of LUFA firmware, the device driver code to implement USB protocol is not needed to be written explicitly. Modifying the firmware code will be sufficient to implement the USB protocol.
PREREQUISTES
This project is based on Arduino Pro Micro which has the USB AVR – Atmega 32u4 as the sitting MCU. In order to understand this project, one must have basic knowledge of the AVR microcontrollers and the embedded C programming for AVRs. WinAVR Studio is used to write, edit and compile the project code, so closely following the project shall require familiarizing with the above stated IDE as well. Though LUFA framework takes care of implementing the USB protocol and has APIs to abstract the lower level codes, understanding USB protocol is recommended to understand how actually the project is working. In fact, if anyone has already worked on some other microcontroller, it will not be much pain to understand and follow this project as the project code is more or less about modifying the LUFA device driver to work as CDC class device and using in-built ADC channels of the microcontroller. One must have additional knowledge of Linux operating system (Ubuntu) and should be knowing basic Linux commands. One having knowledge of Python programming language is recommended but not mandatory for implementing the project.
COMPONENTS REQUIRED
1. Arduino Pro Micro
2. Connecting wires
3. Micro USB cable
SOFTWARE TOOLS REQUIRED
1. WinAVR Studio
2. AVR Dude
5. Pyserial 2.7
Fig. 2: Block Diagram of Arduino based USB Digital Voltmeter
CIRCUIT CONNECTIONS
The project uses Arduino Pro Micro as the USB controller chip and uses Analog In pins for voltage reading. A wire is connected to common ground and four wires are connected through the pin 4, 5, 6 and 7 of Port F. The wires taken from the Analog In pins are used to sense the voltage in a circuit with respect to the ground. The device could read four different voltages at a time in range between 0V and 5V. The Program code for the project is burnt to the Arduino Pro Micro using AVR Dude. The Arduino board is connected to the USB port of a PC by a USB cable.
HOW THE PROJECT WORKS
In this project the USB protocol is implemented by the LUFA framework. For configuring the controller chip to work as CDC class device, the CDC Class Driver of the LUFA framework is used. The CDC is a composite USB device class, and the class may include more than one interface. The CDC is used primarily for modems, but also for ISDN, fax machines, and telephony applications for performing regular voice calls.
When a USB device is attached to the host (PC), the host sends request for configuration details in the form of control transfer. The connected device has to respond with appropriate descriptors to get configured and ready for further operations. Only after configuration, the device can communicate with the host in the form of interrupt, isochronous or bulk transfers for executing the operations for which the device has been made. This process of identification and configuration of the device with the host is called enumeration. The device designed in this project uses control transfer to enumerate with the host and bulk transfer to send voltage data to the computer. The data is exchanged using Class-Specific Requests. These Requests are sent by Host to Device via Control Transfer. Check out the Atmega 32u4 Based USB to UART Converter Project for learning about the functioning of the CDC class devices.
Fig. 3: Image showing working of Arduino based USB Digital Voltmeter
The project is USB CDC class device. In the LUFA framework CDC class related modules are in the LUFA-Source-Folder /LUFA/Drivers/USB/Class/Device folder. Other device class related module are also in the same folder. The LUFA framework has demo projects for different USB device classes in the LUFA-Source-FolderDemosDeviceClassDriver folder. For implementing the project, demo project for virtual serial device provided in the LUFA framework will be modified and complied. The demo project for virtual serial device is in the LUFA-Source-FolderDemosDeviceClassDriverVirtualSerial folder. The folder contains VirtualSerial.c file which will be modified to implement the project.
How VirtualSerial.c identifies device a CDC USB Device
The VirtualSerial.c uses VirtualSerial_CDC_Interface interface in CDC_Device_USBTask () function which is being imported from the CDCDeviceClass.c (from LUFA-Source-Folder LUFADriversUSBClassDevice) to configure the device as USB CDC device. The interface abstracts the low-level descriptor codes and identifies the device as CDC device through an InterfaceNumber variable.
USB CDC SPECIFIC DESCRIPTORS
Check out the Atmega 32u4 Based USB to UART Converter Project for learning about the CDC class device specific descriptors.
From Where VirtualSerial.C gets the USAGE and Data Report Descriptors
In the LUFA framework’s demo project for Virtual Serial, VirtualSerial.c file imports VirtualSerial.h where the descriptors.h is imported. The descriptor.h has the structures defining CDC class related descriptors which have been explained above. The VirtualSerial.c imports VirtualSerial.h which imports usb.h. USB.h imports CDCCLass.h. In CDCClass.h is imported CDCClassDevice.h. The CDCClassDevice.h imports CDCClassCommon.h where the CDC device specific descriptor fields have been defined.
HOW THE DEVICE WORKS
For voltage reading, the wire connected to Analog In pin of the microcontroller will be attached to the voltage source and a wire from common ground will be attached to the reference point. The Analog pins are a hardware functionality of the microcontroller through which analog voltage can be measured. The analog voltage range which Atmega 32u4 can read varies between 0V and 5V.
The microcontroller will then convert the measured Analog voltage to Digital data. For analog to digital conversion, it will use the 10-bit resolution ADC (Analog to Digital Converter). Therefore, it will represent the analog signal in a range of 0-1023. Each Analog input pin will have its own ADC. Generally, the term channel is used to number the ADCs. For example, the Atmega 32u4 has twelve Analog pins and hence twelve ADC channels, 0-11. For four analog readings, four ADC channels numbering from Channel4 to Channel7 will be used. These channels are specifically used because they are available on the Arduino Pro Micro board. However, if Atmega 32u4 is used externally any four out of the twelve channels can be used.
Once the digital data is ready, the device can transmit that data to the PC using bulk transfer. For sending data to computer using bulk transfer, main() function of the VirtualSerial.c will be modified. On PC side, a Python Application will be used to receive data from the microcontroller. The Application will be based on Python programming language and will run on Linux distribution like Ubuntu. It will use Pyusb and Libusb framework for receiving data on the USB protocol. The application sends a channel number through USB request. The data from the respective ADC channel is read and transferred to the PC.
Fig. 4: Image showing voltage readings on python application from Arduino based USB Digital Voltmeter
Check out the program code to see the modifications implemented for the project.
PROGRAMMING GUIDE
For building the project download the LUFA framework from the github.com. The demo project provided with the LUFA framework is modified to make the device. In the extracted LUFA zip file, open Demos/Device/ClassDriver/VirtualSerial folder. The folder has the following files and folders.
Fig. 5: Screenshot of LUFA folder on Windows
Of these, VirtualSerial.h, VirtualSerial.c and Makefile needs to be modified for the project. The modified files (provided at the bottom of the article in zip format) can also be downloaded from the engineersgarage and replaced with the original files. Either open the files in WinAVR Studio or Notepad++ and modify original files or replace files with the already modified one. The modified or replaced VirtualSerial.c needs to be compiled from within the LUFA’s Source folder to get the object code.
Modifying VirtualSerial.h
The VirtualSerial.h library file is imported in the VirtualSerial.c file and includes a set of additional libraries and defines the constants and functions for the joystick device. These include the additional libraries for the joystick, button and LEDs which should be commented out as the project is not using these features. So open VirtualSerial.h and make the following changes -:
• Comment the #include library statements for Joystick.h, LEDS.h, and (These libraries are commented as any joystick, buttons board and LED board is not used on the device)
• Comment the #define statements for LEDMASK_USB_NOTREADY, LEDMASK_USB_ENUMERATING, LEDMASK_USB_READY, LEDMASK_USB_ERROR
• Delete the function declaration for CheckJoystickMovement
Save the file with changes.
Modifying VirtualSerial.C file
Again in the VirtualSerial.c, the code sections for Joystick, button board and LEDs need to be commented out. So open VirtualSerial.c and make the following changes -:
• In the main loop, comment the LEDs_SetAllLEDs()
• In SetupHardware() function, comment the Joystick_Init(), LEDs_Init()
• In EVENT_USB_Device_Connect() function, comment the LEDs_SetAllLEDs()
• In EVENT_USB_Device_Disconnect() function, comment LEDs_SetAllLEDs()
• In EVENT_USB_Device_ConfigurationChanged() function, comment the LEDs_SetAllLEDs()
• Also delete the CheckJoystickMovement function completely
In VirtualSerial.c the main() function executes the functioning of the device. First ADC.h has to be included in the VirtualSerial.c So add the following statements in the beginning of file -:
A file variable has to be defined to contain data received from the USB port. This has to be used as parameter in CDC_Device_CreateStream() function. The CDC_Device_CreateStream() function is defined in the CDCClassDevice.c file where other CDC Class Device related functions are also defined. The CDCClassDevice.c is located in the LUFA-Source-FolderLUFAUSBClassDevice folder.
static FILE USBSerialStream;
A character buffer rxbuff[] to hold serial data is defined. The array representing character buffer has to set its last element as NULL character for which ReportString pointer is defined. A 16-bit integer variable to hold ADC data is declared. Another array printbuff[] is defined to hold strig representation of the ADC data and an additional array to hold new line representation is defined. The ADC is initialized using InitADC() function. An infinite for loop is called in which data from USB port is assigned to OUT_Data variable using getc() function and data (USB Request) from computer is stored in rxbuff[] array. If there is any USB request, analog data is converted to digital form using ReadADC function. The ADC channel from which data has to be read is selected from the desktop application. The channel number is used as parameter to the ReadADC() function. The data is converted to string representation and transmitted to the computer.