Hello,
I'm currently facing issue with drawing data on display. Mostly it works perfectly, but often is drawing nonsense / wrong text data.
I would be happy, if someone could take a look and advise.
I'm using this controller
and this display
Display library used: GxEPD2_750_T7
GxEPD2_BW<GxEPD2_750_T7, GxEPD2_750_T7::HEIGHT> display(GxEPD2_750_T7(/CS=5/ SS, /DC=/17, /RST=/16, /BUSY=/4));
I've written following method to draw meeting data on display. Please bear in mind it is not fully completed yet.
void displayMeetings()
{
String currentDateTime = getCurrentDateTime(); //format: "2024-07-11T11:30:00"
String startUtc = getCurrentDateTimeUTC();
String startTime = getStartTimeUTC(); //startUtc.substring(0,13) + ":00:01Z";
String end = currentDateTime.substring(0, 10) + "T23:59:59Z";
String token = getAccessToken();
MeetingsData meetingData = getMeetings(token, startTime, end);
Meeting *meetings = meetingData.meetings;
int numMeetings = meetingData.numMeetings;
display.setRotation(0);
display.setTextColor(GxEPD_BLACK);
// Display if the room is occupied or available
display.setPartialWindow(150, 130, 400, 60);
display.firstPage();
do {
display.fillRect(150, 130, 400, 60, GxEPD_WHITE); // Clear the area
display.setFont(&FreeSansBold18pt8b);
bool isMeetingInProgress = false;
String endTimeOfCurrentMeeting = "";
int t = 0;
for (int i = 0; i < numMeetings; i++) {
if (currentDateTime >= meetings[i].startTime && currentDateTime <= meetings[i].endTime) {
isMeetingInProgress = true;
endTimeOfCurrentMeeting = format_time_cet(meetings[i].endTime);
t = i;
break;
}
}
if (isMeetingInProgress) {
display.setCursor(160, 180);
display.print(occupiedTill + endTimeOfCurrentMeeting);
}
else if(numMeetings > 0) {
if(currentDateTime < meetings[t].startTime){
display.setCursor(160, 180);
String result = format_time_cet(meetings[t].startTime);
display.print(availableTill + result);
}
else if(currentDateTime > meetings[t].endTime){
display.setCursor(160, 180);
String result = format_time_cet(meetings[t].endTime);
display.print(availableFrom + result);
}
}
else if (numMeetings == 0) {
display.setCursor(250, 180);
display.print(available);
}
} while (display.nextPage());
if(numMeetings > 0){
// Display the next meeting details
display.setPartialWindow(10, 230, 780, 165);
display.firstPage();
do{
int maxEvents = (numMeetings > 2) ? 2 : numMeetings;
display.setTextWrap(true);
display.setFont(&FreeSansBold15pt8b);
int yPos = 260;
display.setCursor(10, yPos);
String start = format_time_cet(meetings[0].startTime);
String end = format_time_cet(meetings[0].endTime);
display.print(start + " - " + end);
display.setCursor(10, 300);
String name = ConvertToISO(meetings[0].name);
display.print(name);
display.setCursor(10, 340);
String organizer = ConvertToISO(meetings[0].organizer);
display.print(organizer);
} while (display.nextPage());
// Display time slots
int squareSize = 60;
int totalSquares = 12;
int partialWindowWidth = 780;
int spacing = (partialWindowWidth - (totalSquares * squareSize)) / (totalSquares - 1);
display.setPartialWindow(10, 400, 780, squareSize + 15);
display.firstPage();
do {
for (int i = 0; i < totalSquares; i++) {
int slotX = 10 + i * (squareSize + spacing);
int slotY = 410;
display.drawRect(slotX, slotY, squareSize, squareSize, GxEPD_BLACK);
String timeSlot = String(7 + i) + ":00 ";
int16_t x1, y1;
uint16_t w, h;
display.setFont(&FreeSans9pt8b);
display.getTextBounds(timeSlot, 0, 0, &x1, &y1, &w, &h);
int centeredX = slotX + (squareSize - w) / 2;
int centeredY = slotY + (squareSize + h) / 2;
display.setCursor(centeredX, centeredY);
display.print(timeSlot);
for (int j = 0; j < numMeetings; j++) {
int startHour = String(format_time_cet(meetings[j].startTime)).substring(0, 2).toInt();
int startMinute = String(format_time_cet(meetings[j].startTime)).substring(3, 5).toInt();
int endHour = String(format_time_cet(meetings[j].endTime)).substring(0, 2).toInt();
int endMinute = String(format_time_cet(meetings[j].endTime)).substring(3, 5).toInt();
int currentHour = 7 + i;
int duration = (endHour * 60 + endMinute) - (startHour * 60 + startMinute);
Serial.println(duration);
if (duration % 30 == 0) {
if ((startHour < currentHour || (startHour == currentHour && startMinute == 0)) &&
(endHour > currentHour || (endHour == currentHour && endMinute > 0))) {
display.fillRect(slotX + 1, slotY + 1, squareSize - 2, squareSize - 2, GxEPD_BLACK);
display.setTextColor(GxEPD_WHITE);
display.setCursor(centeredX, centeredY);
display.print(timeSlot);
display.setTextColor(GxEPD_BLACK);
}
}
}
}
} while (display.nextPage());
}
}
Data which I'm drawing is being converted to 8bit GFX fonts (ISO-8859-2, used for Czech diacritics)
More details here:
https://www.sigmdel.ca/michel/program/misc/gfxfont_8bit_en.html
Here is the function which converts the font data.
String ConvertToISO(const char *input)
{
char text[255];
strcpy(text, input);
utf8tocp(text);
return text;
}
I'm also attaching image of faulty data showed on display. Please note some sections with company sensitive data are intentionally erased (section after Meeting room text and QR code for reservations in the top right corner).
EDIT:
After performing some Serial prints, I've found out the problem is probably not in the font converter as it prints wrong data even before conversion.
Attaching also the method to fetch data from Graph API. I believe the problem is somewhere here
MeetingsData getMeetings(String token, String startTime, String endTime)
{
String payload = "";
String url = "https://graph.microsoft.com/v1.0/users/" + userId + "/calendarView?startDateTime=" + startTime + "&endDateTime=" + endTime;
http.begin(url);
http.addHeader("Authorization", "Bearer " + token);
http.addHeader("Prefer", "outlook.timezone=\"Central European Standard Time\"");
int httpResponseCode = http.GET();
if (httpResponseCode > 0)
{
if (httpResponseCode == HTTP_CODE_OK)
{
payload = http.getString();
//Serial.println(payload);
JsonDocument doc;
deserializeJson(doc, payload);
JsonArray events = doc["value"];
Serial.print("Meeting count:");
Serial.println(events.size());
if (events.size() == 0)
{
http.end();
return { nullptr, 0 };
}
int meetingCount = events.size();
Meeting *meetings = new Meeting[meetingCount];
for (int i = 0; i < events.size(); ++i)
{
JsonObject event = events[i];
const char* subject = event["subject"];
const char* organizer = event["organizer"]["emailAddress"]["name"];
const char* startTime = event["start"]["dateTime"];
const char* endTime = event["end"]["dateTime"];
meetings[i].name = subject;
meetings[i].organizer = organizer;
meetings[i].startTime = startTime;
meetings[i].endTime = endTime;
}
http.end();
return { meetings, meetingCount };
}
else
{
Serial.printf("[HTTP] GET... code: %d\n", httpResponseCode);
}
}
else
{
Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpResponseCode).c_str());
}
http.end();
return { nullptr, 0 };
}
Here is definition for MeetingsData:
#ifndef MEETING_H
#define MEETING_H
#include <string.h>
class Meeting {
public:
const char* name;
const char* organizer;
const char* startTime;
const char* endTime;
};
#endif
and
#ifndef MEETINGDATA_H
#define MEETINGDATA_H
#include <string.h>
#include "meeting.h"
class MeetingsData {
public:
Meeting* meetings;
int numMeetings;
};
#endif