6. Embedded Programming

Trying to control our microcontrollers.

Assignment

Group Assignment

Link to this week's group assignment

Input and Output Devices

The PCB I made is a simple dev board for ATmega328p processor, it's using the internal oscillator of the 328p, so it requires a special configuration for the Arduino IDE and upload the program via a programmer. The process of uploading the code using an Arduino Uno-emulated programmer has been documented on week04.

Below is the ATmega328p connected to the Arduino Uno via SPI interface to be programmed.

This week I uploaded the following program to control the on-board LED with a button.

void setup() {
  pinMode(9, INPUT);
  pinMode(10, OUTPUT);
  pinMode(13, OUTPUT);

  digitalWrite(10, HIGH);
}

void loop() {
  digitalWrite(13, !digitalRead(9));
}

Communication Between Devices

For communication between different devices, me and my teammates tried many different prorocols at the lab.

Serial Communication

First, I did very simple serial communication between the board and the computer, I used an Arduino Uno for simplicity.

At the beginning, I uploaded the following two sketches to the board using Arduino IDE to test the basic functionality.

void setup() {
  Serial.begin(9600); // start serial communication with 9600 baud rate
}
void loop() {
  // send text "hello" through the serial port every second
  Serial.println("hello");
  delay(1000);
}
void setup() {
  Serial.begin(9600);
}
void loop() {
  // loop if the serial port have data to read
  while (Serial.available() > 0) {
    char c = Serial.read(); // read one character from the serial port
    Serial.write(c);        // write the character through serial
  }
}

And both of the two programs worked fine, as the serial port of the Arduino Uno is connect with the computer with the USB connector, the board can send and receive data to and from the computer. The data sent by the board can be viewed with the Serial Monitor of the Arduino IDE on the PC, and PC can also send data with the input box to the board.

Chat program between two boards

Then we want to do serial communication between two boards, also between two Arduino Unos.

We want to do the chat application between two boards, both of them receive input from the computer, then send the message to the other board with the serial connectiono between them, then send the data received from the other board to the computer they were connected to to display the message in their corresponding serial monitor.

(TODO: explain more with diagram)

As it shows above, each board needs two serial port, one to connect with the computer, the other to connect them with each other. But the Arduino Uno only comes with one serial port, and it is connected with the computer through USB.

The Arduino IDE comes with a library for Uno that can address this problem, which is the Software Serial library. It allows us to choose any two GPIO pins to be the TX and RX pin of a software emulated serial port, and we can send data using those two pins just like using a regular serial port.

We started with the example code the Arduino IDE, first select Arduino Uno as the developement board in the menu entry Tools > Board > Arduino AVR Boards > Arduino Uno, then from File > Examples > Software Serial > SoftwareSerialExample, open the example program from Tom Igoe and Mikal Hart.

/*
  Software serial multiple serial test

 Receives from the hardware serial, sends to software serial.
 Receives from software serial, sends to hardware serial.

 The circuit:
 * RX is digital pin 10 (connect to TX of other device)
 * TX is digital pin 11 (connect to RX of other device)

 Note:
 Not all pins on the Mega and Mega 2560 support change interrupts,
 so only the following can be used for RX:
 10, 11, 12, 13, 50, 51, 52, 53, 62, 63, 64, 65, 66, 67, 68, 69

 Not all pins on the Leonardo and Micro support change interrupts,
 so only the following can be used for RX:
 8, 9, 10, 11, 14 (MISO), 15 (SCK), 16 (MOSI).

 created back in the mists of time
 modified 25 May 2012
 by Tom Igoe
 based on Mikal Hart's example

 This example code is in the public domain.

 */
#include <SoftwareSerial.h>

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

void setup() {
  // Open serial communications and wait for port to open:
  Serial.begin(57600);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }


  Serial.println("Goodnight moon!");

  // set the data rate for the SoftwareSerial port
  mySerial.begin(4800);
  mySerial.println("Hello, world?");
}

void loop() { // run over and over
  if (mySerial.available()) {
    Serial.write(mySerial.read());
  }
  if (Serial.available()) {
    mySerial.write(Serial.read());
  }
}

It's an example program basicly doing what we want, so we wrote our code based on the example program, with some small modification. Because the two boards are doing exactly the same operation, they can use the same program. We connected the two boards with their TX and RX pins and thought this will be no problem, but all we got was garbage values showing up in the serial monitor.

After some times of debuging and checking different parts of our program and still didn't fixed the issue, I realized that it was we didn't connect the ground pins of the two boards together, resulting them having different base voltage and couldn't read the correct voltage value between their serial pins. And after we connected the GND pins of the two boards together, we got the correct values, the problem wasn't in our code after all.

#include <SoftwareSerial.h>
// declare second serial port with pin 10 and 11 for RX and TX pin
SoftwareSerial serial2(10, 11); // RX, TX

void setup() {
  Serial.begin(9600);  // start serial port between self and computer with 9600 baud rate
  serial2.begin(4800); // start software serial with other board with 4800 baud rate
}

// string to hold current message from the computer
String current_msg = "";

void loop() {
  // loop while there's data from other board
  while (serial2.available() > 0) {
    // send received data to computer
    char c = serial2.read();
    Serial.write(c);
  }
  // read data input by the computer
  while (Serial.available() > 0) {
    char c = Serial.read();
    if (c == '\n') {
      // newline means we've read the whole message
      // show what we've entered on the serial monitor
      Serial.print("me: ");
      Serial.println(current_msg);
      current_msg = ""; // reset the string
    } else {
      current_msg += c; // append the character to the string
    }
    serial2.write(c); // send the data to other board
  }
}

Apart from the GND problem, we also found that when using the same 57600 baud rate for the hardware serial the same as the example code, we sometime have missing characters. The problem went away when using the 9600 baud rate, it might be that the 57600 baud rate is too fast for the Uno to handle.

The other small inconvience with the example is that you can't see the message that you sent yourself, so I added the functionality to show what we've just typed in the serial monitor.

I2C communictaion

Then I went on doing communication with my other teammate. She tried to use the Xiao RP2040 development board, and we also wanted to implement the same chat program between her board and another device.

At the time of doing the exercise, we consult the Seeed Official Website for the documentation of the serial communication of the Xiao RP2040. And the documentation is a little bit confusing and we thought that the Xiao RP2040 was the same as the Uno that it uses the same serial port to communicate with other devices and the computer. (But later after we examined the data sheet and the source code of the Arduino Core for RP2040, we found that it was not the case.) And we also didn't found example code for software serial for the RP2040, so we thought it doesn't have the functionality and we could not have two serial port simaltinously on the Xiao RP2040 (, which is also not true after we looked through the source code, it turned out that we can just use it with no problem).

Anyway, we thought we can only have one serial port between the computer and Xiao RP2040 so we cannot use serial communictation between the devices. So we decided to try I2C communictaion for this one.

The Xiao and Arduino Uno all have I2C capability. We based our code on the examples that come with the Wire library.

(TODO: Document program detatil)


The final code on the Xiao RP2040 and the Arduino Uno are shown below.

/* This is the sketch that is uploaded on the Xiao RP2040.
 * The Xiao acts as a master device on the I2C bus to send and
 * receive message from the slave at address 8.
 */

#include <Wire.h>

String input = "";

void setup() {
  Wire.begin();        // join I2C bus (address optional for master)
  Serial.begin(9600);  // start serial for output
}

void loop() {
  Wire.beginTransmission(8);
  Wire.write(0);
  Wire.endTransmission();
  int l = Wire.requestFrom(8, 1);
  if (l == 1) {
    char n = Wire.read();
    if (n > 0) {
      Wire.beginTransmission(8);
      Wire.write(1);
      Wire.endTransmission();
      Wire.requestFrom(8, n);     // request 6 bytes from slave device #8
      while (Wire.available()) {  // slave may send less than requested
        char c = Wire.read();     // receive a byte as character
        Serial.print(c);          // print the character
      }
    }
  }

  if (Serial.available() > 0) {
    Wire.beginTransmission(8);
    Wire.write(2);
    while (Serial.available() > 0) {
      char c = Serial.read();
      Wire.write(c);

      input += c;
      if (c == '\n') {
        Serial.print("me: ");
        Serial.print(input);
        input = "";
      }
    }
    Wire.endTransmission();
  }
}
/* This sketch is uploaded on the Arduino Uno.
 * It acts as the slave device at address 8 on the I2C bus.
 */

#include <Wire.h>

String msg = "";
String input = "";
int mode;

void recv(int count) {
  char c = Wire.read();
  if (c == 0 || c == 1) {
    mode = c;
  } else if (c == 2) {
    while (Wire.available() > 0) {
      char c = Wire.read();
      Serial.write(c);
    }
  }
}

void req() {
  if (mode == 0) {
    Wire.write(msg.length());
  } else if (mode == 1) {
    Wire.write(msg.c_str(), msg.length());
    msg = "";
  }
}

void setup() {
  Serial.begin(9600);

  Wire.begin(8);
  Wire.onReceive(recv);
  Wire.onRequest(req);
}

void loop() {
  while (Serial.available() > 0) {
    char c = Serial.read();
    msg += c;
    input += c;
    if (c == '\n') {
      Serial.print("me: ");
      Serial.print(input);
      input = "";
    }
  }
}