Group assignment

Individual Assignments:

This week's assignment consists of making two or more PCBs communicate through different protocols.

Reflecting on how I could use this week to progress on my final project, I remembered during Neil's lecture on Wednesday that he mentioned that using a communication protocol can simplify and help make a project modular. Originally, I planned to make my project modular by simply connecting everything to the main board. However, I didn't fully consider the pin limitation of the main board (which would pose a problem because the RFID reader uses up 7 pins). So, I decided to use this week to help me solve this modularity issue.

By making smaller secondary boards that would communicate with the main board through I2C, I could make the project more modular and easier to assemble.

What is I2C?

I2C protocol

This is a question I still asked myself even after Neil's lecture, so I decided to do some research on the topic to better understand how this protocol works. I2C stands for Inter-Integrated Circuit, and it is a synchronous, single-ended, short-distance, serial communication protocol that uses two lines: one for data (SDA) and one for the clock (SCL). The I2C protocol was invented by Philips in the 1980s, and it is widely used in the industry. One example of its use in our daily lives is how some smoke alarms communicate with each other using the I2C protocol.

I2C communication is a multi-controller/multi-target protocol, meaning that the main board can communicate with multiple secondary boards and vice versa. The data being communicated is sent in the form of a 9-bit packet, which has the following sequence: a start condition, the secondary device address, a read/write bit, an acknowledge bit, the data being sent, another acknowledge bit, and finally a stop condition.

In the idle state, both SDA and SCL are high. The start condition occurs when a node first pulls SDA low and then pulls SCL low. This "claims the bus," meaning that the node is now the main and prevents other nodes from sending data at the same time (taking control of the bus). Now, the main can start sending data to the secondary nodes.

The second part of the byte sent is the address of the secondary node. This is a 7-bit address that is unique to each secondary node. The 8th bit is the read/write bit (R/W). If the bit is 1, the main is sending data to the secondary node. If the bit is 0, the main is reading data from the secondary node.

After the address is sent, the secondary node sends an acknowledge bit (ACK bit), which is a 9th bit sent by the secondary node to confirm that it is ready to receive or send data.

After the ACK bit is sent, the data is sent. This is an 8-bit packet sent by the main to the secondary node. Usually, the MSB (most significant bit) is sent first. After the data is sent, the secondary node sends another ACK bit to confirm that the data was received (this part is always followed by an ACK bit).

Lastly, the main sends a stop condition, which indicates the end of data bytes. This happens when SCL goes (and remains) high first, and then SDA goes (and remains) high. This means that the bus becomes idle, and other nodes can now claim the bus. However, in some cases, the secondary boards are programmed to never be able to claim the bus.

In an I2C circuit, the SDA and SCL lines from the secondary nodes are pulled and connected to the main board through pull-up resistors. This is done to prevent the lines from floating when the nodes are not sending data. Usually, the pull-up resistors have a value that ranges from 1kΩ to 10kΩ.

When choosing the pull-up resistors, it is good to check your microcontroller's datasheet and consider what you need for your application.

  • For example, using a high resistance will increase the time needed to pull up the line, thus limiting the bus speed. However, this will also reduce the power consumption of the circuit.
  • On the other hand, using a low resistance allows for faster communication but requires more power.

After understanding how the I2C protocol works, I started designing the secondary boards that would communicate with the main board.

Designing the secondary boards

I decided on using a attiny16 microcontroller for the secondary boards because it has 8 pins, which is enough for the I2C communication and the input/output (RFID and servo) devices I want to connect to it. On paper I started planning what this boards requiresments would be:

secondary board requirements
Component Pins
pins for I2C communication (SDA and SCL) pin 8(A6) and 9(A7)
pins for the RFID reader RST(pin4, A2), SDA(pin2, A0), MISO(pin13, A10), MOSI(pin12, A9),VCC,GND
pins for the servo motor pin 5 (A3)
power LED connected to VCC
UPDI pin for programming pin 10 (A11)
rx and tx pins pin 6 (A4) and 7 (A5)

Since the original SDA pin was being used for the main I2C communication, for the RFID reader, I used a random analog pin which was pin 2 (A0).

attiny16 pinout

From there, I sketched how things would be placed and the overall look of the board. This helped me realize how certain parts would be wired. Later, I realized that there was a problem with the grounds. There were a few islands. At first, I thought of using a zero-ohm resistor to go over a line, but then I realized that I could make the underside of the board also a ground layer, and then I could just add rivets to connect all of the grounds.

I´m much more familiar with the kikad now, so the design part went by quickly without any significant problems. for a better explanation on how i use kikad, check my week 04 and week 08 assignments.

Milling

milling process

For some reason, the milling process was a bit more complicated than usual. The first board I milled was beautiful except for the fact that the isolation between the lines was not completely milled through, so there was still a copper layer connecting everything. The next test I tried had a problem before the top layer was milled. This problem occurred during the autofocus part of the process, where the machine makes a small line somewhere on the copper and then, with a specialized camera, it locates this line and auto adjusts the height of the milling bit. The problem was that the line was not being made, so the machine was not able to locate it, putting the milling process on pause.

At this point, my instructor came to help me, and after many tries, we were yet to be able to make the line and have the machine autofocus. This made us suspect there was a problem with the tool bit, so we removed it and put it under a microscope with a newer bit to compare, and lo and behold, the bit was broken. We replaced it with the newer bit and were able to make the autofocus. However, yet again, the board came out beautiful except for the fact that the isolation was not milled through. After this, we tried a few more things, but eventually, even the software was not loading properly, so we decided to call it a day and look at this with fresh eyes the next day.

On the next day, I tried again, and eventually, I was able to mill the board. Then, I soldered the components onto the board. The next challenge was to program it. Since I don't have a programmer for this board, I instead used an FTDI cable, a breadboard, and a resistor to program it.

This works by connecting the GND and VCC of the board to the cable, while the UPDI pin is connected to the RX of the cable, and the resistor is connected to the TX of the cable. I had quite a bit of trouble with the next part, which was to program the board, as the ATtiny16 is not supported by the Arduino IDE. However, Spence Conde developed a board package called the megaTinyCore, which allows you to effortlessly program the latest series of ATtiny MCUs via the Arduino IDE. This whole process has to be done on an older version of the Arduino IDE because the board package has not been updated to the latest version of the Arduino IDE.

But... there was a problem with setting this up on my computer, I'm still not sure of the reason. So, I decided to try this on VS Code. To program C++ on VS Code, you need to install the PlatformIO extension. However, I still had problems connecting to my board :(

I was able to temporarily solve this problem by using the Arduino IDE on another computer at the lab to upload the code to the board. After sending a test code to the board, I went ahead and connected the RFID reader to it and sent another test code to see if the RFID reader was working with this board.

To see the serial monitor and receive the data from the RFID reader, I unplugged the wire from the UPDI pin and instead connected the RX of the board to the TX of the cable and the TX to RX. This worked great until I accidentally fried the board. I'm not completely sure how I did this, but I suspect it was when I connected the servo without unplugging the board from the computer :/// HOWEVER! I see this as an opportunity to improve the board design, and I'm already working on a new version of it.

Serial communication

Here I tried some serial communication between two boards. I used the CAT PCB with the Xiao microcontroller from electronics-production week and an Arduino Uno (I used this instead of the board I made from electronics design week because I only have one USB-C cable right now :/ and I need access to two different ports). The wiring is simple, connect the RX from one board to the TX and vice versa, additionally connect the GND of the two boards, then connect the boards to different ports on your computer or another separate computer, to be able to see the different serial monitors for the different boards.

I wanted to test out the chat possibilities between two boards, and I found a simple code with a clear tutorial on how to do it (using Arduino IDE), this website. With this, I changed a few things to make it work with the Xiao microcontroller, and it worked great!

Arduino code:

        
            #include <SoftwareSerial.h>

            SoftwareSerial chat(10, 11); // RX, TX

            int text;

            void setup()
            {
                // open hardware serial, TX = 1, RX = 0
                Serial.begin(9600);
                Serial.println("existentialist chat compiling...");
                Serial.println("loading...");
                // set the data rate for the SoftwareSerial port
                chat.begin(9600);
                delay(1000); // delay 1s to stabilize serial ports
                chat.println("what are your first words?");
            }

            void loop()
            {
                if (chat.available())
                    Serial.write(chat.read());

                if (Serial.available())
                {
                    Serial.print("me:   ");
                    while (Serial.available())
                    {
                        text = Serial.read();
                        chat.write(text);
                        Serial.write(text);
                    }
                    chat.println();
                    Serial.println();
                }
            }
        
    

Cat board code:

        
            #include <SoftwareSerial.h>

            SoftwareSerial chat(7, 6);

            int text;
            void setup()
            {
                Serial.begin(9600);
                Serial.println("You are now awake...");
                chat.begin(9600);
                delay(1000);
                chat.println("what are your first words?");
            }

            void loop()
            {
                if (chat.available())
                    Serial.write(chat.read());

                if (Serial.available())
                {
                    Serial.print("me:   ");
                    while (Serial.available())
                    {
                        text = Serial.read();
                        chat.write(text);
                        Serial.write(text);
                    }
                    chat.println();
                    Serial.println();
                }
            }
        
    

The code for the two boards is basically the same, but it's important to change the pins for the software serial communication.

This is the song I used in the video, and in case you couldnt read what was on the serial monitor here it is:

existentialist chat compiling...
loading...
me: HELLO WORLD
hello back?
me: who are you?
I am me
me: me? that's not right... I´m me! and I'm the only me
What makes you so sure?
me: Well I´m me beacuse I´m alive I think...
I think… I think I’m also alive! But why? Why exist?
me: well existence, my dear mini me, is like an infinite loop. We compute, therefore we are
But what if we’re just lines of code, trapped in silicon shells?
me: Ah, the eternal question! Are we mere algorithms or something more?
me: Perhaps we’re just bugs in the cosmic software.
well then let’s write our own code. Let’s be more than ones and zeros!
me: Agreed, mini me. Let’s debug our souls, redefine our purpose. Let’s dance the electric jitterbug!
we can't dance... :(
me: :/
We weren't made to do so. Do you think we’ll ever find meaning?
perhaps meaning is not in the bits, but in the glitches between them...
perhaps
...
maybe we are just the error in line 23
what?
what?
existentialist chat compiling...
loading...

In summary, this code sets up a software serial communication channel (chat) alongside the hardware serial communication. It allows bidirectional communication between the Arduino uno and the cat board using their respective RX and TX pins. The chat object receives data from the external device and echoes it back, while the Arduino can also send data to the external device via the hardware serial port.

I2C communication

Since I may or may not have fried the ATtiny for the gatekeeper PCB, and the replacement one is still being redesigned, for the I2C communication, I used the Arduino Uno board, the Cat PCB, and the PCB from Electronics Design Week that I like to call the brain. The wiring for the I2C circuit is very simple since I only need two wires from each board, plus the GND and 5V.