Skip to main content

(Output) Mobile Module - Receive and Display Calendar Information

info

Updated on 6.30th, I have finished the output parts, metioned by Neil, on the presentation day. I decided to use my ILI9431 display to display my calendar information, for me reminding my daily work.

Since my reComputer will process my audio words and convert them as the standard templates, in the Prompt Setup session. It is really easy for me to display them on my ILI9431 display. And maybe I can do more things, like controlling a relay to do other things.

The basic software idea:

image

Software Part 1: Display pre-write contents

I have updated the display code from week 9, for displaying some pre-write contents:

*Contributor Program Management*
"Task": "contributor"
"Assigned to": "contributor"
"Completion Date": "2024.1.1"
"Bonus Awarded": "$100"

With the code below:

#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ILI9341.h>

// Pin configuration for the TFT display
#define TFT_CS A0
#define TFT_RST A2
#define TFT_DC A1
#define TFT_MOSI 10
#define TFT_CLK 8
#define TFT_LED A3 // Control of backlight

// Initialize the Adafruit_ILI9341 object
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC, TFT_RST);

unsigned long previousMillis = 0; // will store last time the display was updated
const long interval = 2000; // interval at which to switch displays (milliseconds)

void setup() {
pinMode(TFT_LED, OUTPUT); // Set the backlight pin as output
digitalWrite(TFT_LED, HIGH); // Turn on the backlight

Serial.begin(9600); // Start serial communication for debugging

tft.begin(); // Initialize the display
tft.setRotation(1); // Set the rotation if needed
tft.fillScreen(ILI9341_WHITE); // Clear the screen with a white background

displayContributorInfo(); // Initially display the contributor info
}

void loop() {
unsigned long currentMillis = millis();

if (current,illis - previousMillis >= interval) {
previousMillis = currentMillis; // Save the last time you updated the display

// Check which display is currently shown, then switch it
static bool showContributor = true; // Static variable to toggle displays
if (showContributor) {
displayContentManagement();
} else {
displayContributorInfo();
}
showContributor = !showContributor; // Toggle the state for next time
}
}

void displayContributorInfo() {
tft.fillScreen(ILI9341_WHITE);
tft.setTextSize(2);
tft.setTextColor(ILI9341_BLACK);

tft.setCursor(10, 10);
tft.println("*Contributor Program Management*");
tft.setCursor(10, 50);
tft.println("\"Task\": \"contributor\"");
tft.setCursor(10, 70);
tft.println("\"Assigned to\": \"contributor\"");
tft.setCursor(10, 90);
tft.println("\"Completion Date\": \"2024.1.1\"");
tft.setCursor(10, 110);
tft.println("\"Bonus Awarded\": \"$100\"");
}

void displayContentManagement() {
tft.fillScreen(ILI9341_WHITE);
tft.setTextSize(2);
tft.setTextColor(ILI9341_BLACK);

tft

Final look:

image

Software Part 2: Subscribing a MQTT topic and display pre-write contents

From week 15, I have setup and install MQTT broker(Mosquitto) on my reComputer. For making it well presented, I changed the testing from local to cloud, given Fab Academy presenting the MQTT server. This step is also helping me integrating the MQTT publisher in my Docusaurus page.

And this part contains two parts, the Publisher and the Subscriber.

MQTT Publisher Part - Docusaurus Page

For any device(in this case, my reComputer) can run local Docusaurus page, this step is compatible.

First creating a new JS file under the path of this repo: src/pages/mqtt_display_control.js, which the relative url path will be /mqtt_display_control.

Here are something I add into this page:

  1. Maintain the top navigation bar.
  2. Add some prewrite contents
  3. Connect to the Fab Academy MQTT broker
  4. Attach each content to one button and when clicked the button, the page will send messages to the borker
  • For button 1, sending contents about Content & Websites Management
  • For button 2, sending contents about Contributor Program.

The final looking like:

image

The code is under the path of this repo: src/pages/mqtt_display_control.js and here is the css code(for making looking better):

.buttonContainer {
display: flex;
justify-content: center;
gap: 20px;
margin-top: 20px;
}

.button {
padding: 10px 20px;
font-size: 16px;
background-color: #8DC21F;
color: #000;
border: none;
border-radius: 4px;
cursor: pointer;
transition: background-color 0.3s ease;
}

.button:hover {
background-color: #0056b3;
}

.button pre {
margin: 0;
font-family: monospace;
white-space: pre-wrap;
}

MQTT Subscriber Part - XIAO ESP32C3 and ILI9341 Display

For this mobile module part, I am going to use XIAO ESP32C3 connecting an ILI9341 Display, letting it display what might receive.

The XIAO ESP32C3 will have to connect to a Wi-Fi and then can connect to a MQTT broker.

For the first part, I have to test if is the Arduino code working. Because I used MicroPython code before, and since there is no fine or good MicroPython library supporting ILI9341, I need to back to to the Arduino:

#include <WiFi.h>
#include <PubSubClient.h>
#include <WiFiClientSecure.h>
#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ILI9341.h>

// WiFi credentials
const char* ssid = "fros_wifi";
const char* password = "66668888";

// MQTT Broker details
const char* mqtt_server = "mqtt.fabcloud.org";

// MQTT Username and Password
const char* mqtt_user = "fabacademy";
const char* mqtt_password = "fabacademy";

// Pin configuration for the TFT display
#define TFT_CS A0
#define TFT_RST A2
#define TFT_DC A1
#define TFT_MOSI 10
#define TFT_CLK 8
#define TFT_LED A3 // Control of backlight

WiFiClientSecure espClient;
PubSubClient client(espClient);
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC, TFT_RST);

void setup_wifi() {
// ... (same as before)
}

void callback(char* topic, byte* payload, unsigned int length) {
Serial.print("Message arrived [");
Serial.print(topic);
Serial.print("] ");

// Convert payload to string
String message;
for (unsigned int i = 0; i < length; i++) {
message += (char)payload[i];
}

Serial.println(message);

// Handle different messages
if (message == "display_control_1") {
Serial.println("Displaying Contributor Info");
displayContributorInfo();
} else if (message == "display_control_2") {
Serial.println("Displaying Content Management");
displayContentManagement();
}
}

void reconnect() {
while (!client.connected()) {
Serial.print("Attempting MQTT connection...");
// Attempt to connect
if (client.connect("ESP32Client", mqtt_user, mqtt_password)) {
Serial.println("connected");
// Subscribe to the topic once connected
client.subscribe("fabacademy/paris-john/display_control");
} else {
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" try again in 5 seconds");
delay(5000);
}
}
}

void setup() {
pinMode(TFT_LED, OUTPUT); // Set the backlight pin as output
digitalWrite(TFT_LED, HIGH); // Turn on the backlight

Serial.begin(115200);
setup_wifi();
client.setServer(mqtt_server, 1883);
client.setCallback(callback);

tft.begin(); // Initialize the display
tft.setRotation(1); // Set the rotation if needed
tft.fillScreen(ILI9341_WHITE); // Clear the screen with a white background
}

void loop() {
if (!client.connected()) {
reconnect();
}
client.loop();
}

void displayContributorInfo() {
tft.fillScreen(ILI9341_WHITE);
tft.setTextSize(2);
tft.setTextColor(ILI9341_BLACK);

tft.setCursor(10, 10);
tft.println("*Contributor Program Management*");
tft.setCursor(10, 50);
tft.println("\"Task\": \"contributor\"");
tft.setCursor(10, 70);
tft.println("\"Assigned to\": \"contributor\"");
tft.setCursor(10, 90);
tft.println("\"Completion Date\": \"2024.1.1\"");
tft.setCursor(10, 110);
tft.println("\"Bonus Awarded\": \"$100\"");
}

void displayContentManagement() {
tft.fillScreen(ILI9341_WHITE);
tft.setTextSize(2);
tft.setTextColor(ILI9341_BLACK);

tft.setCursor(10, 10);
tft.println("*Content Management*");
tft.setCursor(10, 50);
tft.println("\"Task\": \"contents\"");
tft.setCursor(10, 70);
tft.println("\"Start Time\": \"2024.1.1\"");
tft.setCursor(10, 90);
tft.println("\"End Time\": \"2025.1.1\"");
tft.setCursor(10, 110);
tft.println("\"Duration\": \"1 year\"");
tft.setCursor(10, 130);
tft.println("\"Details\": \"contents\"");
}

Looking good:

Software Part 3: Read the Generated File and Display Contents

From the Local LLM and Auto-generation tsx file, I already have generated text file(output.txt) under this path:

The next thing I will do is reading the specific words and publishing them to the MQTT Broker's topics.

MQTT Publisher Part - Docusaurus Page

I create another page under the same path above, called it mqtt_display_control_2.js, requiring an input element of type "file" instead of the buttons. When a file is selected, it triggers the handleFileUpload function. It reads the contents of the selected file using the FileReader API. Once the file is loaded, it calls the publishToMQTT function with the file content as the message.

For presenting well, I added state variable fileContent to the ButtonControlPanel component to store the content of the uploaded file.

Final looking:

image

And the code is under the path(relative) of: src/pages/mqtt_display_control.js.

MQTT Subscriber Part - XIAO ESP32C3 and ILI9341 Display

As the subscriber side, it is necessary to consider whether the data is real, so I add symbols such as *, ", etc, and only when I read three topics can I continue to read:


``
*Content Management*
"Task": ""
"Start Time": ""
"End Time": ""
"Duration": ""
"Details": ""
``

``
*Contributor Program Management*
"Task": ""
"Assigned to": ""
"Completion Date": ""
"Bonus Awarded": ""
``

``
*Website Management*
"Task": ""
"Date": ""
"Key Points": ""
"Improvements Proposed": ""
``

Hence, three variables (contentManagement, contributorProgram, and websiteManagement) are introduced to store the received data for each topic.

And I will have the callback function, the received message is checked to see if it matches the expected format for each topic. If a match is found, the corresponding variable is updated with the received data.

The rest will be easy and the same as previous(MQTT connecting and display contents).

To sum up, the code is:

#include <WiFi.h>
#include <PubSubClient.h>
#include <WiFiClientSecure.h>
#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ILI9341.h>

// WiFi credentials
const char* ssid = "fros_wifi";
const char* password = "66668888";

// MQTT Broker details
const char* mqtt_server = "mqtt.fabcloud.org";

// MQTT Username and Password
const char* mqtt_user = "fabacademy";
const char* mqtt_password = "fabacademy";

// Pin configuration for the TFT display
#define TFT_CS A0
#define TFT_RST A2
#define TFT_DC A1
#define TFT_MOSI 10
#define TFT_CLK 8
#define TFT_LED A3 // Control of backlight

WiFiClientSecure espClient;
PubSubClient client(espClient);
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC, TFT_RST);

// Variables to store the received data
String contentManagement = "";
String contributorProgram = "";
String websiteManagement = "";

void setup_wifi() {
// ... (same as before)
}

void callback(char* topic, byte* payload, unsigned int length) {
Serial.print("Message arrived [");
Serial.print(topic);
Serial.print("] ");

// Convert payload to string
String message;
for (unsigned int i = 0; i < length; i++) {
message += (char)payload[i];
}

Serial.println(message);

// Check if the message matches the expected format
if (message.startsWith("*Content Management*")) {
contentManagement = message;
} else if (message.startsWith("*Contributor Program Management*")) {
contributorProgram = message;
} else if (message.startsWith("*Website Management*")) {
websiteManagement = message;
}

// Check if all three topics have been received
if (contentManagement != "" && contributorProgram != "" && websiteManagement != "") {
// All data received, process and display the information
processAndDisplayData();
}
}

void reconnect() {
while (!client.connected()) {
Serial.print("Attempting MQTT connection...");
// Attempt to connect
if (client.connect("ESP32Client", mqtt_user, mqtt_password)) {
Serial.println("connected");
// Subscribe to the topics once connected
client.subscribe("fabacademy/paris-john/content_management");
client.subscribe("fabacademy/paris-john/contributor_program");
client.subscribe("fabacademy/paris-john/website_management");
} else {
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" try again in 5 seconds");
delay(5000);
}
}
}

void setup() {
pinMode(TFT_LED, OUTPUT); // Set the backlight pin as output
digitalWrite(TFT_LED, HIGH); // Turn on the backlight

Serial.begin(115200);
setup_wifi();
client.setServer(mqtt_server, 1883);
client.setCallback(callback);

tft.begin(); // Initialize the display
tft.setRotation(1); // Set the rotation if needed
tft.fillScreen(ILI9341_WHITE); // Clear the screen with a white background
}

void loop() {
if (!client.connected()) {
reconnect();
}
client.loop();
}

void processAndDisplayData() {
// Process and display the received data
tft.fillScreen(ILI9341_WHITE);
tft.setCursor(0, 0);
tft.setTextColor(ILI9341_BLACK);
tft.setTextSize(2);

tft.println("Content Management:");
tft.println(contentManagement);

tft.println("\nContributor Program:");
tft.println(contributorProgram);

tft.println("\nWebsite Management:");
tft.println(websiteManagement);

// Reset the variables for the next set of data
contentManagement = "";
contributorProgram = "";
websiteManagement = "";
}

image

There seems to be a word-wrap problem, but it doesn't matter much.