Skip to content

11. Input devices

Introduction

This week I worked on designing a board that featured an input device of my choice. As I wanted to make progress on my final project, I decided to design, mill, solder, and program a board that featured an on-board RFID module and the necessary pins that enable serial communication between an Arduino and a Raspberry Pi. This feature is crucial for my final project to work, as many of the features that will be included in the final iteration of my product can exclusively operate on a raspberry pi, both due to the modicum of processing power offered by most microcontrollers as well as the ease of running different programming languages, such as Python, on a Raspberry Pi.

Assignment

This week’s individual assignment was to “measure something” by adding a sensor to a microcontroller board that I have designed and to read the input from the sensor. The group assignment for this week was to “probe an input device’s analog levels and digital signals,” which we accomplished by way of an oscilloscope. All of my work for this week as well as the final files that I used are included at the bottom of this page.

Week 11 Individual Assignment

The individual assignment for this week was to attach a sensor to a microcontroller board that I have designed throughout the course of Fab. As the only board that I had designed prior to this week was my simple button board that I used during electronics production week, I needed to start over with a new microcontroller and configuration in order to enable the RFID and RXD/TXD serial communication capabilities that I envisioned for this week’s assignment. As I would need a modern microcontroller and a preponderance of pins for my project to function properly, I initially decided to design my board using the ATMega328P microcontroller, which is a trusty and current board that features a 4x8 pin configuration.

pg1

To begin designing my board, I opened my PCB design software of choice, Autodesk EAGLE, and created a new schematic file. Next, I added several of the key components that I knew would be required for my board to function properly. These components included the schematic for the ATMega328P microcontroller, which I located in the atmel.lbr library, an SMD regulator that I would need to regulate the 5V output from the chip to the 3.3V required by my RFID module, and a 4x2 array of header pins that I would use to program the board via AVR. As I wanted to ensure the durability of this board, I decided to use through-hole header pins for this first time. While surface mount headers are effective, I have had experience with them snapping off of a board and pulling up pads after significant use, which necessitates the production of an entirely new board in most cases. However, through-hole header pins are much more stable, as they are attached to the board with significantly more security than their surface-mount counterparts.

pg1

Next, I added a 22pF capacitor that would be connected to the 5V and GND connectors of my board, which would allow for the flow of much more stable power throughout the entirety of my board. For larger boards in the future, I will place these capacitors near each point where components interact with high voltages, as these locations are the most volatile parts of any board.

pg1

After adding header pins for the connection of both my RFID module and the Raspberry Pi, my first schematic was complete. For the RFID module to work, I needed to connect many pins that need to be connected to other components of the board, particularly the AVR headers, as the AVR uploading protocol requires connections to MOSI, MISO, SCL, SCK, GND, and VCC headers, which would certainly complicate my circuitry. The Raspberry Pi simply needs to be connected to the RXD and TXD pins on the board to communicate with the microcontroller via serial communication.

pg1

Final iteration of my board with an ATMega328P microcontroller before I switched to the ATTiny1614.

After attempting to route the traces for this board in a .brd file that I generated from the schematic, I concluded that any further efforts to fix this board would prove futile. As such, I decided to switch microcontrollers, electing to utilize an ATTiny1614, which featured all of the pins that I desired in a smaller form factor alongside support for the incredibly simple jtag uploading protocol, which only requires three pins to successfully perform uploads to a microcontroller. As a result, I would be able to route my traces far more efficiently, without the need to bifurcate traces to reach different ends of my PCB.

pg1

As the fab_new.lbr library did not have a schematic for the ATTiny1614 microcontroller, I needed to download a library that did offer the correct board so I could route traces for the board in my schematic file and prepare the board for milling in a .brd file. After searching several libraries that claimed to offer the microcontroller but actually did not, I eventually discovered the official avr-7.lbr library which does offer a schematic for the ATTiny1614 microcontroller. All of the other components that I would be using are offered in the fab_new.lbr library, which made the process of designing the schematic for this board almost identical to that of my design process for my previous ATMega328P microcontroller that I originally planned to use.

’pee’

In order to prevent my .brd file from becoming a rat’s nest that would be almost impossible to route traces for once I placed all of my components, I decided to route traces for each component in my .brd file after placing it in my schematic file.

’pee’
’pee’

Next, I placed an LED and a 499 ohm resistor in order to troubleshoot my board in the future. Connecting the LED between 5V and GND would allow me to check if my board was receiving power. As I generally find that I experience problems with powering my boards rather than writing to individual pins, I figured routing the LED in this manner would divulge the most useful information to me regarding the state of my board following its completion.

pg1

Next, I placed the same 4x2 through-hole header array that I had on my original board that used an ATMega328P microcontroller, which again would be used for the on-board RFID module. At this point, I decided to re-route my traces to add some more space to my board. While making the traces close together could save lots of material at an industrial scale when thousands of boards are being manufactured, making my boards smaller could realistically only save several square inches of my board at best, which does not outweigh the significant advantage that larger traces make when soldering in my opinion. As such, I decided to expand the footprint of my board to allow for several key components to be placed further apart in order to mitigate bridging as well as the omnipresent risk of ripping thin traces that is exacerbated when the traces are closer together and thinner.

pg1

I needed to route two 0 ohm jumper resistors in order to bring SDA and SCL from the ATTiny1614 to my 4x2 header pin array that would be used on my RFID module once my board was complete. The module will be located on an external board due to the location that it needs to be within my final project to consistently scan RFID cards.

pg1

After routing all of my traces that I had on the board after placing the 0 ohm resistors, I realized that the configuration of traces near the 4x2 was incredibly messy and could be optimized to improve the ease of soldering my board and also mitigate the likelihood of traces ripping off of my board.

pg1

I decided to clean up all of the traces on my board before adding the 2x1 headers for RDX and TDX serial communication that I will be using later on when creating my final program for my final project that will rely heavily on certain features that are uniquely available on the RPi.

pg1

After cleaning up all of my traces, I added a 2x1 SMD header pin array that will eventually be used to enable serial communication on my board to a RPI. Also, my board does show several shorts because of inconsistencies between my schematic and board file that I only edited in my .brd file. These shorts show up on the .brd file because the linking between .sch and .brd files in EAGLE is only .sch -> .brd, meaning that when I made changes to just the .brd file, these changes did not display in the .brd file. As attempting to rectify each short in my .sch file just to prevent some red lines from displaying on my .brd file would constitute a needless waste of time, I decided to ignore the red lines and carry on with my work for this week. Once my file was done, I was ready to export my file from EAGLE and download it onto a machine in our lab that features the BanTam tools software and a connection to one of our four OtherMill PCB Milling machines. My workflow for using these machines is documented in extensive detail on my page from electronics production week.

Milling

’pee’
’pee’

After importing my .brd file into the BanTam tools software, the tools that the software calculates toolpaths for default to a 1/32” End Mill Bit; however, due to the several very thin traces that needed to be on my board due to the density of traces in certain areas of the board, I needed to add a second, more precise bit to the calculations that would be used to cut all of the sections of the board that the 1/32” bit cannot reach. I generally use a 0.005” engraving bit for all of the smaller traces on my boards, but even with this bit added to the tools the board would still be impossible to mill in certain areas, which are highlighted in red in the above screenshots. Even with a 0.003” engraving bit added, which is the smallest bit in our lab, certain areas of the file were still not possible to mill out. In order to ensure the success of my board, I needed to redesign the .brd file and increase the size of the traces that prevented my file from working properly. After increasing the thickness of the problematic traces, I returned to the milling machines and began milling the first iteration of my input board.

’wait’
’soon’

I encountered several problems whilst milling my first input board. Due to the precision of the milling machines, each copper blank must be situated almost perfectly level on the milling plate to ensure a successful cut, as small fragments of copper can remain on the board if they are even several thousandths of an inch lower than the level that the machine is attempting to cut at. This small fallacy rendered two of my input boards entirely unusable, which necessitated re-milling until I manufactured a board without any visible copper remnants due to leveling issues. Due to the closeness of the traces on my board, I decided to remove the excess copper along the edge of the board by maximizing the ‘trace clearance’ setting in the BanTam Tools software. This strategy has proven incredibly useful thus far in my Fab experience, as I have found that it is useful in mitigating the likelihood of shorts forming between close components when soldering, as solder adheres to traces rather than central “islands” of copper that will short the board if connecting to multiple traces. While maximizing the trace clearance theoretically reduces the durability of a board as the traces are not secured by copper on each side, though I have not experienced any problems with traces ripping from my boards as long as I handle the board with a reasonable degree of care.

Soldering

pg1

The process of soldering my board was relatively straightforward as the functionality of the board was only dependent on the proper orientation of two components, including my voltage regulator and the microcontroller. For the remainder of the surface mount components on my board, I simply soldered one pad to the component, securing it to the board, and then soldered the remaining side of the component to the vacant side on the board. After repeating this process for each component on the board, I needed to secure the header pins to the pads and holes that I milled on my board. After securing the three different arrays of header pins to the board, I was ready to begin testing my board.

Testing my board

As the ATTiny1614 microcontroller that I included in my board is part of the megaTiny series of microcontrollers from Atmel, I would need to program my board via the ‘UPDI’ uploading protocol rather than simply using the ArduinoISP programmer that is used to program many other microcontrollers. Programming microcontrollers in the megaTiny series of boards requires the megaTinyCore Arduino library to be imported into the Arduino IDE. As I had already used UPDI to program my boards that used ATTiny412 microcontrollers during electronics production week and electronics design week, I already had all of the necessary resources installed on my machine to successfully program my board via the UPDI uploading protocol.

1614board

The only difference between the programming process for my input board was the board that I selected in the megaTinyCore library. For this board, I selected the ‘ATTiny1614’ in the ‘boards’ section of the Arduino IDE. As I would be programming my board through an Arduino, I simply connected jumper wires between 5V and GND on my Arduino and the 5V and GND pins on the FTDI header array that I included on my custom board. As UPDI only requires a single data connection to perform uploads, I simply connected pin 6 on my Arduino to the UPDI header that I included on the FTDI header array on my 1614 board. As my RFID module had not arrived at the time of preliminary tests, I decided to simply upload the basic example ‘blink.ino’ sketch to the board to see if I could successfully program my board. The board did program successfully, but since I did not include an onboard LED on the 1614 board, I did not receive any visual confirmation that the code was being executed as intended.

Voltage Regulator

pg1

When I designed my board, I used the above schematic as a reference for the routing of my traces through the regulator in order to safely power my RFID module via a 3.3V connection. The voltage regulator's datasheet estimates that the regulator can function for roughly 1,000 hours, which is likely far more time than I will use my input board for. multimeterphoto As I was using a fixed voltage regulator, I simply connected a 100 nF capacitor between the 5V output on my microcontroller, though the voltage value could be anything within the regulator’s limitations for it to function properly, and GND and also connected a 10 uF capacitor via the 3.3V output on the voltage regulator and GND on my microcontroller. Other, non-fixed capacitors perform logic based on the above capacitance values to determine the output voltage, but fixed voltage regulators like the one that I used on this board will always output the same voltage that is specified on the regulator’s respective datasheet. While my regulator would have functioned properly if the above schematic accurately reflected the pinout of the voltage regulator, the actual pinout of the regulator that I used is different from the schematic that is presented on the regulator’s datasheet. As I did not check the pinout of the regulator before designing my board, I was not getting 3.3V out of the regulator as each connection that I made was routed to the wrong pin on the regulator. I've attached a complete PDF of the datasheet for the ld1117v33 regulator that I used below. An online version of the datasheet can be accessed via this link.

multimeterphoto

After discovering that I was only getting 0.4V out my regulator, I knew that I had incorrectly routed my traces through the regulator, but before I redesigned my board I performed various tests with the same regulator to verify the function of each of the three pins on the regulator. To accomplish this task, I used an Arduino and a voltage regulator soldered to a proto board, and connected the necessary components on a breadboard using through-hole capacitors. testingproto

After my testing concluded, I discovered that the pinout was actually GND, 3.3V, 5V and not 5V, GND, 3.3V.

pg1

I redesigned my board in EAGLE to reflect the accurate pinout of the voltage regulator. No other changes were necessary. After redesigning my board, I followed the same process of milling and soldering that I did with my original input board.

pg1

Programming

’pee’
’boardmakemehappy’

The programming process for my input board was very similar to that of the previous boards that I have made throughout Fab that used ATTiny412 microcontrollers, as both the ATTiny1614 and ATTiny412 use the same programming protocol to upload to the boards, UPDI. Uploading to microcontrollers from the ATTiny series requires the megaTinyCore library. As I already had the library installed from previous Fab assignments, I simply changed the board in the ‘boards’ section of the library and selected the ATTiny1614 board. I first decided to test my board by uploading a basic blink sketch to the board to check if it could actually perform an upload; however, as I did not include an LED connected to data on my board, I would not be able to verify that the microcontroller was actually working. Thankfully, my code did upload, so I assumed that the board was working and began preparing to upload a slightly modified example code from the RFID library to verify that my RFID module would work with my board. In order to prevent the example sketch from writing to the RFID cards, I commented out the sections of the code where the write function from the RFID library was invoked.

/**
 * ----------------------------------------------------------------------------
 * This is a MFRC522 library example; see https://github.com/miguelbalboa/rfid
 * for further details and other examples.
 *
 * NOTE: The library file MFRC522.h has a lot of useful info. Please read it.
 *
 * Released into the public domain.
 * ----------------------------------------------------------------------------
 * This sample shows how to read and write data blocks on a MIFARE Classic PICC
 * (= card/tag).
 *
 * BEWARE: Data will be written to the PICC, in sector #1 (blocks #4 to #7).
 *
 *
 * Typical pin layout used:
 * -----------------------------------------------------------------------------------------
 *             MFRC522      Arduino       Arduino   Arduino    Arduino          Arduino
 *             Reader/PCD   Uno/101       Mega      Nano v3    Leonardo/Micro   Pro Micro
 * Signal      Pin          Pin           Pin       Pin        Pin              Pin
 * -----------------------------------------------------------------------------------------
 * RST/Reset   RST          9             5         D9         RESET/ICSP-5     RST
 * SPI SS      SDA(SS)      10            53        D10        10               10
 * SPI MOSI    MOSI         11 / ICSP-4   51        D11        ICSP-4           16
 * SPI MISO    MISO         12 / ICSP-1   50        D12        ICSP-1           14
 * SPI SCK     SCK          13 / ICSP-3   52        D13        ICSP-3           15
 *
 */

#include <SPI.h>
#include <MFRC522.h>

#define RST_PIN          11          // Configurable, see typical pin layout above
#define SS_PIN            6        // Configurable, see typical pin layout above

MFRC522 mfrc522(SS_PIN, RST_PIN);   // Create MFRC522 instance.

MFRC522::MIFARE_Key key;

/**
 * Initialize.
 */
void setup() {
    Serial.begin(9600); // Initialize serial communications with the PC
    while (!Serial);    // Do nothing if no serial port is opened (added for Arduinos based on ATMEGA32U4)
    SPI.begin();        // Init SPI bus
    mfrc522.PCD_Init(); // Init MFRC522 card

    // Prepare the key (used both as key A and as key B)
    // using FFFFFFFFFFFFh which is the default at chip delivery from the factory
    for (byte i = 0; i < 6; i++) {
        key.keyByte[i] = 0xFF;
    }

    Serial.println(F("Scan a MIFARE Classic PICC to demonstrate read and write."));
    Serial.print(F("Using key (for A and B):"));
    dump_byte_array(key.keyByte, MFRC522::MF_KEY_SIZE);
    Serial.println();

    Serial.println(F("BEWARE: Data will be written to the PICC, in sector #1"));
}

/**
 * Main loop.
 */
void loop() {
    // Reset the loop if no new card present on the sensor/reader. This saves the entire process when idle.
    if ( ! mfrc522.PICC_IsNewCardPresent())
        return;

    // Select one of the cards
    if ( ! mfrc522.PICC_ReadCardSerial())
        return;

    // Show some details of the PICC (that is: the tag/card)
    Serial.print(F("Card UID:"));
    dump_byte_array(mfrc522.uid.uidByte, mfrc522.uid.size);
    Serial.println();
    Serial.print(F("PICC type: "));
    MFRC522::PICC_Type piccType = mfrc522.PICC_GetType(mfrc522.uid.sak);
    Serial.println(mfrc522.PICC_GetTypeName(piccType));

    // Check for compatibility
    if (    piccType != MFRC522::PICC_TYPE_MIFARE_MINI
        &&  piccType != MFRC522::PICC_TYPE_MIFARE_1K
        &&  piccType != MFRC522::PICC_TYPE_MIFARE_4K) {
        Serial.println(F("This sample only works with MIFARE Classic cards."));
        return;
    }

    // In this sample we use the second sector,
    // that is: sector #1, covering block #4 up to and including block #7
    byte sector         = 1;
    byte blockAddr      = 4;
    byte dataBlock[]    = {
        0x01, 0x02, 0x03, 0x04, //  1,  2,   3,  4,
        0x05, 0x06, 0x07, 0x08, //  5,  6,   7,  8,
        0x09, 0x0a, 0xff, 0x0b, //  9, 10, 255, 11,
        0x0c, 0x0d, 0x0e, 0x0f  // 12, 13, 14, 15
    };
    byte trailerBlock   = 7;
    MFRC522::StatusCode status;
    byte buffer[18];
    byte size = sizeof(buffer);

    // Authenticate using key A
    Serial.println(F("Authenticating using key A..."));
    status = (MFRC522::StatusCode) mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, trailerBlock, &key, &(mfrc522.uid));
    if (status != MFRC522::STATUS_OK) {
        Serial.print(F("PCD_Authenticate() failed: "));
        Serial.println(mfrc522.GetStatusCodeName(status));
        return;
    }

    // Show the whole sector as it currently is
    Serial.println(F("Current data in sector:"));
    mfrc522.PICC_DumpMifareClassicSectorToSerial(&(mfrc522.uid), &key, sector);
    Serial.println();

    // Read data from the block
    Serial.print(F("Reading data from block ")); Serial.print(blockAddr);
    Serial.println(F(" ..."));
    status = (MFRC522::StatusCode) mfrc522.MIFARE_Read(blockAddr, buffer, &size);
    if (status != MFRC522::STATUS_OK) {
        Serial.print(F("MIFARE_Read() failed: "));
        Serial.println(mfrc522.GetStatusCodeName(status));
    }
    Serial.print(F("Data in block ")); Serial.print(blockAddr); Serial.println(F(":"));
    dump_byte_array(buffer, 16); Serial.println();
    Serial.println();

    // Authenticate using key B
    Serial.println(F("Authenticating again using key B..."));
    status = (MFRC522::StatusCode) mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_B, trailerBlock, &key, &(mfrc522.uid));
    if (status != MFRC522::STATUS_OK) {
        Serial.print(F("PCD_Authenticate() failed: "));
        Serial.println(mfrc522.GetStatusCodeName(status));
        return;
    }

//    // Write data to the block
//    Serial.print(F("Writing data into block ")); Serial.print(blockAddr);
//    Serial.println(F(" ..."));
//    dump_byte_array(dataBlock, 16); Serial.println();
//    status = (MFRC522::StatusCode) mfrc522.MIFARE_Write(blockAddr, dataBlock, 16);
//    if (status != MFRC522::STATUS_OK) {
//        Serial.print(F("MIFARE_Write() failed: "));
//        Serial.println(mfrc522.GetStatusCodeName(status));
//    }
//    Serial.println();

    // Read data from the block (again, should now be what we have written)
    Serial.print(F("Reading data from block ")); Serial.print(blockAddr);
    Serial.println(F(" ..."));
    status = (MFRC522::StatusCode) mfrc522.MIFARE_Read(blockAddr, buffer, &size);
    if (status != MFRC522::STATUS_OK) {
        Serial.print(F("MIFARE_Read() failed: "));
        Serial.println(mfrc522.GetStatusCodeName(status));
    }
    Serial.print(F("Data in block ")); Serial.print(blockAddr); Serial.println(F(":"));
    dump_byte_array(buffer, 16); Serial.println();
//
//    // Check that data in block is what we have written
//    // by counting the number of bytes that are equal
//    Serial.println(F("Checking result..."));
//    byte count = 0;
//    for (byte i = 0; i < 16; i++) {
//        // Compare buffer (= what we've read) with dataBlock (= what we've written)
//        if (buffer[i] == dataBlock[i])
//            count++;
//    }
//    Serial.print(F("Number of bytes that match = ")); Serial.println(count);
//    if (count == 16) {
//        Serial.println(F("Success :-)"));
//    } else {
//        Serial.println(F("Failure, no match :-("));
//        Serial.println(F("  perhaps the write didn't work properly..."));
//    }
//    Serial.println();

    // Dump the sector data
    Serial.println(F("Current data in sector:"));
    mfrc522.PICC_DumpMifareClassicSectorToSerial(&(mfrc522.uid), &key, sector);
    Serial.println();

    // Halt PICC
    mfrc522.PICC_HaltA();
    // Stop encryption on PCD
    mfrc522.PCD_StopCrypto1();
}

/**
 * Helper routine to dump a byte array as hex values to Serial.
 */
void dump_byte_array(byte *buffer, byte bufferSize) {
    for (byte i = 0; i < bufferSize; i++) {
        Serial.print(buffer[i] < 0x10 ? " 0" : " ");
        Serial.print(buffer[i], HEX);
    }
}
multimeterphoto

The process of connecting my RFID module to the board was relatively simple, as I simply cross-referenced the traces on my board with the marks on the RFID that denote which pin to connect to each header of the module. multimeterphoto While connecting my RFID module to my board with jumper wires, I discovered that my header array did not include a connection to SCK, which is required for the SPI protocol to communicate with my microcontroller. As I did not want to mill an entirely new board because of one simple error, I simply soldered a spare jumper wire directly onto the vacant SCK pin of my microcontroller and connected it to the ‘SCK’ header of the RFID module.

EVERYTHING CONNECTED

After discovering that my board would only receive uploads with the RFID module disconnected from the board (really only RST, because UPDI uses the same pin on the 1614), I uploaded an example sketch to the RFID module. Because the Arduino that I was using was not receiving any serial communications from the board when I scanned an RFID card over the reader, I decided to use an FTDI cheap in place of the Arduino, which would allow me to read the bare sensor output from the module without any intermediary interference caused by other subsystems within the Arduino.

After connecting 5V, GND, and TX/RX to my FTDI board, I was able to verify that serial data was able to be transmitted via my board to the FTDI after placing a basic print statement at the top of the main method in my code, though I was still unable to print the UID and other information of an RFID card when scanning the card on my RFID module. After cross-referencing the pinout of my ATTiny1614 with the pins that were connected to my RFID, I realized that I had input an incorrect value for the reset pin. After changing the reset pin value from 10 to 11 within my code, I was able to monitor the correct pins and print UID’s and other information from RFID cards when scanning them with resounding success. A video of the RFID module functioning properly and outputting data to serial is enclosed below.

Input Board Working from Jack Hollingsworth on Vimeo.

Next Steps

After successfully printing RFID data to serial via my input board, I have accomplished an incredibly important component of my final project, as the entirety of my final project relies on accurate data output by serial from the RFID module. Over the course of the following weeks, I will work on interpreting the data from my RFID module in order to continue the development of my final cryptocurrency payment service. The remaining objectives that I need to achieve with this data is transmitting it to a Pi, checking UID’s against a database of all of the unique UID’s that are linked to individual Bitcoin addresses and execute console commands to authorize cryptocurrency transactions using the data that is pulled from the RFID cards that are scanned on my RFID module. While I do need to create a Pi interface as an output for my final project.

Conclusion

While I had used EagleCAD many times throughout Fab prior to this week, my input board was the first board that I made throughout my Fab experience that was designed and fabricated without the influence of my peers or other parties, which was not the case during electronics design week as each of my peers decided to fabricate boards with a button and an LED that were used to toggle the onboard LED by detecting the press of the button via the ATTiny412’s data pins. Ultimately, the troubleshooting and testing phases of this project significantly enhanced my knowledge and capabilities within EAGLE, and I also made an important stride in my progress towards completing my final for Fab Academy, as my final project will rely heavily on a slightly modified version of the board that I designed over the past week.

Group Work

For this week's group work, I worked on probing inputs with an oscilloscope. Grant Fleischer recorded the videos that are on the group site and Drew Griggs wrote the majority of this week's site. The site can be accessed here. Download this week's files! (24 kB)

Important Links


Last update: June 6, 2021