The todolist website I often use is todoist.com, and I often use this website for task recording and application development planning. I want to develop an application based on RP2040 and WiFi module WizFi360, which can synchronize with the website and display the current task list on a screen.
“todoist” is the most popular app of organize work and life. The todoist app has more than 30 million users.
“todoist” provide Two open APIs for users to call their own data.
Rest APIThe Todoist REST API offers the simplest approach to read and write data on the Todoist web service.
For most common application requirements this is our recommended API for external developers and uses an approach that should be familiar for anyone with experience calling RESTful APIs.
We've provided a Getting Started tutorial section within our REST API documentation to introduce you to the API and some common flows you'll encounter when developing Todoist applications. If you are totally new to our APIs or even unfamiliar with Todoist this is a great place to start.
Sync APIThe Todoist Sync API is used by our web and mobile applications and is recommended for clients that will maintain a local representation of a user's full account data. It allows for incrementally sending and receiving account data by identifying positions in an account's state history using a sync token.
This API also contains additional endpoints for areas like account management and less common features that may not yet be implemented on the newer REST API.
Within the documentation for the Sync API you'll also find a Getting Started tutorial that will introduce you to using the Sync endpoint to send and receive data.
The detailed introduction of the api is in the following link:https://developer.todoist.com/guides/
Although the name of the project I developed is “Todolist Sync Monitor”, it uses the RSET api because it is more convenient.
I use wizfi360 as http client to send todolist RSET API request and get todolist parameters. WizFi360 is a WiFi module, which can connect to WiFi through commands and perform TCP or TCP (SSL) connections. I have used it many times and it is very convenient.
RP2040 acts as an MCU, after get todolist info from wizfi360, it performs data processing and displays the content on the screen.
This project is divided into five steps:
Step 1: Create New Account on the Todolist website and get API TOKEN;
Step2: Install library files and board support in the Arduino IDE;
Step 3: Get parameters from Todolist API through WizFi360;
Step 4: Displays the Todolist info on the screen(ILI9488);
Step 5: Finish the task on Todolist Sync Monitor.
The following are step by step instructions.
Step 1: Create New Account on the Todolist website and get API TOKEN;After creat account in this website, you can see your API TOKEN on the "My accounts" page. Please record it, because this TOKEN will be required for future page visits.
in "Setting">>"Integrations">>"API token". you will get your "API token",
Your API token provides full access to view and modify your Todoist data. Please treat this like a password and take care when sharing it.
Add "WIZnet WizFi360-EVB-PICO" support to Arduino IDE
Open up the Arduino IDE and go to File->Preferences.
In the dialog that pops up, enter the following URL in the "Additional Boards Manager URLs" field:
https://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json
Search "WizFi360" and Install Board support by "Board Manager"
"Tools->Board:"***"-> Raspberry Pi RP2040 Boards(2.6.1) " Select “WIZnet WizFi360-EVB-PICO”.
Add “GFX Library for rduino”, this library support the screen ili9488.
Because we need to display the Todolist's icon, we need to load a PNG library“PNGdec” to decode the image.
Step 3: Get parameters from Todolist API through WizFi360;#include "WizFi360.h"
// Wi-Fi info //
char ssid[] = "WIZNET_test"; // your network SSID (name)//
char pass[] = "********"; // your network password//
int status = WL_IDLE_STATUS; // the Wifi radio's status//
// Initialize the Ethernet client object
WiFiClient client;
Initialize serial port for WizFi360 module and change the baudrate to 2000000bps(MAX baudrate for wizfi360).
The first initialization is 115200, and then setting the baud rate (2000000) is added to the initialization part of the WiZfi360 library, and the second time is changed to 2000000bps.
// initialize serial for WizFi360 module
Serial2.setFIFOSize(4096);
Serial2.begin(2000000);
WiFi.init(&Serial2);
Check the wizfi360 Link status of wifi in the “void setup()”
// check for the presence of the shield
if (WiFi.status() == WL_NO_SHIELD) {
while (true);// don't continue
}
// attempt to connect to WiFi network
while ( status != WL_CONNECTED) {
status = WiFi.begin(ssid, pass);// Connect to WPA/WPA2 network
}
“void loop()” There are 9 cases in The switch statement.
typedef enum
{
send_project_request = 0,
get_project_list,
send_sections_request,
get_sections_list,
send_task_request,
get_task_list,
display_todolist,
todolist_close_task,
show_close_result,
}STATE_;
STATE_ currentState;
- 3.1 case "
send_project_request
" link to todoist server and send a GET project request.
case send_project_request:
{
// if you get a connection, report back via serial
if (client.connectSSL(todoist_server,443)) {
delay(3000);
// Make a HTTP request
client.println(String("GET /rest/v2/projects HTTP/1.1"));
client.println(String("Host:") + String(todoist_server));
client.println(String("Authorization: Bearer ") + String(todoist_token));
client.println("Connection: close");
client.println();
json_String= "";
currentState = get_project_list;
}
else
{
client.stop();
delay(1000);
}
}
break;
Get all projects:
Get all projects:
$ curl -X GET \
https://api.todoist.com/rest/v2/projects \
-H "Authorization: Bearer $token"
Example response:
Example response:
[
{
"id": "220474322",
"name": "Inbox",
"comment_count": 10,
"order": 0,
"color": "grey",
"is_shared": false,
"is_favorite": false,
"is_inbox_project": true,
"is_team_inbox": false,
"view_style": "list",
"url": "https://todoist.com/showProject?id=220474322",
"parent_id": null,
}
]
- 3.2 case "
get_project_list
" Process the obtained "PROJECT"information.
case get_project_list:
{
while (client.available()) {
json_String += (char)client.read();
data_now =1;
}
if(data_now)
{
#ifdef debug_msg
Serial.println(json_String);
#endif
for(uint8_t i=0; i<todoist_project_max; i++)
{
todoist_project[i].Exist = true;
dataStart = json_String.indexOf("\"id\": \"",dataEnd) + strlen("\"id\": \"");
dataEnd = json_String.indexOf("\",", dataStart);
todoist_project[i].ID = json_String.substring(dataStart, dataEnd);
#ifdef debug_msg
Serial.print("todoist_project.ID:");
Serial.println(todoist_project[i].ID);
#endif
dataStart = json_String.indexOf("\"color\": \"", dataEnd) + strlen("\"color\": \"");
dataEnd = json_String.indexOf("\",", dataStart);
todoist_project[i].Color_Str = json_String.substring(dataStart, dataEnd);
#ifdef debug_msg
Serial.print("todoist_project.Color_Str:");
Serial.println(todoist_project[i].Color_Str);
#endif
dataStart = json_String.indexOf("\"name\": \"", dataEnd) + strlen("\"name\": \"");
dataEnd = json_String.indexOf("\",", dataStart);
todoist_project[i].Name = json_String.substring(dataStart, dataEnd);
#ifdef debug_msg
Serial.print("todoist_project.Name:");
Serial.println(todoist_project[i].Name);
#endif
dataStart = json_String.indexOf("\"url\": \"", dataEnd) + strlen("\"url\": \"");
dataEnd = json_String.indexOf("\",", dataStart);
todoist_project[i].Url = json_String.substring(dataStart, dataEnd);
#ifdef debug_msg
Serial.print("todoist_project.Url:");
Serial.println(todoist_project[i].Url);
#endif
if((dataEnd+40)> json_String.length())
{
todoist_project_num = i+1;
data_now =0;
client.stop();
delay(3000);
currentState = send_sections_request;
return;
}
}
}
}
break;
The obtained PROJECT information includes project ID, name, and color etc.
struct _todoist_project{
boolean Exist = false;
String ID;
String Name;
String Color_Str;
} ;
_todoist_project todoist_project[todoist_project_max];
uint8_t todoist_project_num = 0;
uint8_t todoist_project_cnt = 0;
The obtained color information is a text description, which needs to be converted to RGB565 color through the following function.
uint32_t Str2RGB565(String str)
{
uint32_t RGB565;
if(str == "berry_red") {RGB565 = 0x8800;}
else if(str == "red") {RGB565 = 0xF800;}
else if(str == "orange"){RGB565 = 0xFD20;}
else if(str == "yellow"){RGB565 = 0xFFE0;}
else if(str == "olive_green") {RGB565 = 0x8400;}
else if(str == "lime_green") {RGB565 = 0x07E0;}
else if(str == "green") {RGB565 = 0x0400;}
else if(str == "mint_green") {RGB565 = 0x3666;}
else if(str == "teal") {RGB565 = 0x0410;}
else if(str == "sky_blue") {RGB565 = 0x867D;}
else if(str == "light_blue"){RGB565 = 0xAEDC;}
else if(str == "blue") {RGB565 = 0x001F;}
else if(str == "grape") {RGB565 = 0x8010;}
else if(str == "violet") {RGB565 = 0x881F;}
else if(str == "lavender"){RGB565 = 0xE73F;}
else if(str == "magenta") {RGB565 = 0xF81F;}
else if(str == "salmon") {RGB565 = 0xFC0E;}
else if(str == "charcoal"){RGB565 = 0xD69A;}
else if(str == "grey") {RGB565 = 0x8410;}
else if(str == "taupe") {RGB565 = 0xBC71;}
else {RGB565 = 0xFFFF;}
return RGB565;
}
This is the color chart:
- 3.3 case "
send_sections_request
"link to todoist server and send a GET sections request.
case send_sections_request:
{
if (client.connectSSL(todoist_server,443)) {
delay(3000);
// Make a HTTP request
client.println(String("GET /rest/v2/sections HTTP/1.1"));
client.println(String("Host: ") + todoist_server);
client.println(String("Authorization: Bearer ") + String(todoist_token));
client.println("Connection: close");
client.println();
json_String= "";
currentState = get_sections_list;
}
else
{
client.stop();
delay(1000);
}
}
break;
Get all sections:
Get all sections:
$ curl -s -H "Authorization: Bearer $token" \
https://api.todoist.com/rest/v2/sections
Example response:
Example response:
[
{
"id": "7025",
"project_id": "2203306141",
"order": 1,
"name": "Groceries"
}
]
- 3.4 case"
get_sections_list
"Process the obtained "SECTION"information.
case get_sections_list:
{
while (client.available()) {
json_String += (char)client.read();
data_now = 1;
dataEnd = 0;
}
if(data_now)
{
#ifdef debug_msg
Serial.println(json_String);
#endif
for(uint8_t m=0; m<todoist_sections_max; m++)
{
todoist_sections[m].Exist = true;
dataStart = json_String.indexOf("\"id\": \"",dataEnd) + strlen("\"id\": \"");
dataEnd = json_String.indexOf("\",", dataStart);
todoist_sections[m].ID = json_String.substring(dataStart, dataEnd);
#ifdef debug_msg
Serial.print("todoist_sections.ID:");
Serial.println(todoist_sections[m].ID);
#endif
dataStart = json_String.indexOf("\"project_id\": \"",dataEnd) + strlen("\"project_id\": \"");
dataEnd = json_String.indexOf("\",", dataStart);
todoist_sections[m].Project = json_String.substring(dataStart, dataEnd);
#ifdef debug_msg
Serial.print("todoist_sections.Project:");
Serial.println(todoist_sections[m].Project);
#endif
dataStart = json_String.indexOf("\"name\": \"",dataEnd) + strlen("\"name\": \"");
dataEnd = json_String.indexOf("\"", dataStart);
todoist_sections[m].Name = json_String.substring(dataStart, dataEnd);
#ifdef debug_msg
Serial.print("todoist_sections.Name:");
Serial.println(todoist_sections[m].Name);
#endif
if((dataEnd+40)> json_String.length())
{
todoist_sections_num =m+1;
data_now =0;
client.stop();
delay(3000);
currentState = send_task_request;
return;
}
}
}
}
break;
The obtained SECTION information includes Section ID, name, and project etc.
struct _todoist_sections{
boolean Exist = false;
String ID;
String Name;
String Project;
};
_todoist_sections todoist_sections[todoist_sections_max];
uint16_t todoist_sections_num;
uint16_t todoist_sections_cnt = 0;
- 3.5 case"
send_task_request
" link to todoist server and send a GET task request.
case send_task_request:
{
if (client.connectSSL(todoist_server,443)) {
delay(3000);
// Make a HTTP request
client.println(String("GET /rest/v2/tasks HTTP/1.1"));
client.println(String("Host: ") + todoist_server);
client.println(String("Authorization: Bearer ") + String(todoist_token));
client.println("Connection: close");
client.println();
json_String= "";
currentState = get_task_list;
}
else
{
client.stop();
delay(1000);
}
}
break;
Get active tasks:
Get active tasks:
$ curl -X GET \
https://api.todoist.com/rest/v2/tasks \
-H "Authorization: Bearer $token"
Example response:
Example response:
[
{
"creator_id": "2671355",
"created_at": "2019-12-11T22:36:50.000000Z",
"assignee_id": "2671362",
"assigner_id": "2671355",
"comment_count": 10,
"is_completed": false,
"content": "Buy Milk",
"description": "",
"due": {
"date": "2016-09-01",
"is_recurring": false,
"datetime": "2016-09-01T12:00:00.000000Z",
"string": "tomorrow at 12",
"timezone": "Europe/Moscow"
},
"id": "2995104339",
"labels": ["Food", "Shopping"],
"order": 1,
"priority": 1,
"project_id": "2203306141",
"section_id": "7025",
"parent_id": "2995104589",
"url": "https://todoist.com/showTask?id=2995104339"
},
...
]
- 3.6 case"
get_task_list
"Process the obtained "TASK"information.
case get_task_list:
{
while (client.available()) {
json_String += (char)client.read();
data_now = 1;
dataEnd = 0;
}
if(data_now)
{
dataStart = json_String.indexOf("Content-Length: ") + strlen("Content-Length: ");
dataEnd = json_String.indexOf("\r\n", dataStart);
dataStr = json_String.substring(dataStart, dataEnd);
dataStart = json_String.indexOf("\r\n\r\n", dataEnd);
todoist_task_len = dataStr.toInt()-(json_String.length() - dataStart - 4);//HexStr2Int(dataStr)-(json_String.length() - dataEnd - 4);
#ifdef debug_msg
Serial.print("todoist_task_len:");
Serial.println(todoist_task_len);
#endif
while(todoist_task_len)
{
while (client.available()) {
json_String += (char)client.read();
todoist_task_len --;
}
}
#ifdef debug_msg
Serial.println(json_String);
#endif
for(uint8_t n=0; n<todoist_task_max; n++)
{
todoist_task[n].Exist = true;
dataStart = json_String.indexOf("\"id\": \"",dataEnd) + strlen("\"id\": \"");
dataEnd = json_String.indexOf("\",", dataStart);
todoist_task[n].ID = json_String.substring(dataStart, dataEnd);
#ifdef debug_msg
Serial.print("todoist_task.ID:");
Serial.println(todoist_task[n].ID);
#endif
dataStart = json_String.indexOf("\"section_id\": ",dataEnd) + strlen("\"section_id\": ")+1;
dataEnd = json_String.indexOf(",", dataStart)-1;
todoist_task[n].Section = json_String.substring(dataStart, dataEnd);
#ifdef debug_msg
Serial.print("todoist_task.section_id:");
Serial.println(todoist_task[n].Section);
#endif
dataStart = json_String.indexOf("\"parent_id\": ",dataEnd) + strlen("\"parent_id\": ")+1;
dataEnd = json_String.indexOf(",", dataStart)-1;
todoist_task[n].Parent = json_String.substring(dataStart, dataEnd);
#ifdef debug_msg
Serial.print("todoist_task.Parent_id:");
Serial.println(todoist_task[n].Parent);
#endif
dataStart = json_String.indexOf("\"content\": \"",dataEnd) + strlen("\"content\": \"");
dataEnd = json_String.indexOf("\",", dataStart);
todoist_task[n].Name = json_String.substring(dataStart, dataEnd);
#ifdef debug_msg
Serial.print("todoist_task.Name:");
Serial.println(todoist_task[n].Name);
#endif
dataStart = json_String.indexOf("\"is_completed\": ",dataEnd) + strlen("\"is_completed\": ");
dataEnd = json_String.indexOf(",", dataStart);
dataStr = json_String.substring(dataStart, dataEnd);
#ifdef debug_msg
Serial.print("todoist_task.Finished:");
Serial.println(dataStr);
#endif
if(dataStr == "true"){todoist_task[n].Finished = true;}
dataEnd = json_String.indexOf("\"url\": ",dataEnd) + strlen("\"url\": ");
if((dataEnd+60)> json_String.length())
{
todoist_task_num =n+1;
data_now =0;
client.stop();
currentState = display_todolist;
return;
}
}
}
}
break;
The obtained TASK information includes task ID, name, Section and Parent etc.
struct _todoist_task{
boolean Exist = false;
boolean Finished;
String ID;
String Section;
String Parent;
String Name;
};
_todoist_task todoist_task[todoist_task_max];
So far, we have obtained project, section and task information respectively. The next step is to handle the display task.
Step 4: Displays the Todolist info on the screen(ILI9488);#include <Arduino_GFX_Library.h>
Arduino_GFX *tft = create_default_Arduino_GFX();
define of pin which is used by ILI9488 in the "libraries\GFX_Library_for_Arduino\src\Arduino_GFX_Library.h"
#elif defined(ARDUINO_RASPBERRY_PI_PICO)||defined(ARDUINO_WIZNET_WIZFI360_EVB_PICO)||defined(ARDUINO_WIZNET_5100S_EVB_PICO)
#define DF_GFX_SCK 26
#define DF_GFX_MOSI 27
#define DF_GFX_MISO GFX_NOT_DEFINED
#define DF_GFX_CS 25
#define DF_GFX_DC 23
#define DF_GFX_RST 28
#define DF_GFX_BL 22
define library used by ILI9488 in the "libraries\GFX_Library_for_Arduino\src\Arduino_GFX_Library.cpp"
#include "Arduino_GFX_Library.h"
#define lCD_ILI9488
Arduino_DataBus *create_default_Arduino_DataBus()
{
#if defined(ARDUINO_ARCH_NRF52840)
return new Arduino_NRFXSPI(DF_GFX_DC, DF_GFX_CS, DF_GFX_SCK, DF_GFX_MOSI, DF_GFX_MISO);
//#elif defined(ARDUINO_RASPBERRY_PI_PICO) || defined(ARDUINO_RASPBERRY_PI_PICO_W)
// return new Arduino_RPiPicoSPI(DF_GFX_DC, DF_GFX_CS, DF_GFX_SCK, DF_GFX_MOSI, DF_GFX_MISO, spi0);
#elif defined(ARDUINO_RASPBERRY_PI_PICO)|| defined(ARDUINO_RASPBERRY_PI_PICO_W) ||defined(ARDUINO_WIZNET_WIZFI360_EVB_PICO)||defined(ARDUINO_WIZNET_5100S_EVB_PICO)
return new Arduino_RPiPicoSPI(DF_GFX_DC, DF_GFX_CS, DF_GFX_SCK, DF_GFX_MOSI, DF_GFX_MISO, spi1);
#elif defined(ESP32)
return new Arduino_ESP32SPI(DF_GFX_DC, DF_GFX_CS, DF_GFX_SCK, DF_GFX_MOSI, DF_GFX_MISO);
#elif defined(ESP8266)
return new Arduino_ESP8266SPI(DF_GFX_DC, DF_GFX_CS);
#else
return new Arduino_HWSPI(DF_GFX_DC, DF_GFX_CS);
#endif
}
Arduino_GFX *create_default_Arduino_GFX()
{
Arduino_DataBus *bus = create_default_Arduino_DataBus();
#if defined(WIO_TERMINAL)
return new Arduino_ILI9341(bus, DF_GFX_RST, 1 /* rotation */);
#elif defined(ARDUINO_RASPBERRY_PI_PICO)|| defined(ARDUINO_RASPBERRY_PI_PICO_W) ||defined(ARDUINO_WIZNET_WIZFI360_EVB_PICO)||defined(ARDUINO_WIZNET_5100S_EVB_PICO)
{
#if defined (lCD_ILI9488)
{
return new Arduino_ILI9488_18bit(bus, DF_GFX_RST, 1 /* rotation */, false /* IPS */);
}
#else
{
return new Arduino_GC9A01(bus, DF_GFX_RST, 0 /* rotation */, true /* IPS */);
}
#endif
}
#elif defined(ESP32_S3_BOX)
return new Arduino_ILI9342(bus, DF_GFX_RST, 0 /* rotation */);
#elif defined(M5STACK_CORE)
return new Arduino_ILI9342(bus, DF_GFX_RST, 2 /* rotation */);
#elif defined(ODROID_GO)
return new Arduino_ILI9341(bus, DF_GFX_RST, 3 /* rotation */);
#elif defined(TTGO_T_WATCH)
return new Arduino_ST7789(bus, DF_GFX_RST, 0 /* rotation */, true /* IPS */, 240, 240, 0, 80);
#else
return new Arduino_ILI9341(bus, DF_GFX_RST, 0 /* rotation */);
#endif
}
Initialize the screen and open the backlight of the screen in the “void setup()”
tft->begin();
tft->fillScreen(WHITE);
pinMode(22, OUTPUT);
digitalWrite(22, HIGH);
- 4.1 Display the "Cloud Pixel" logo in the first interface on the screen.
void display_logo(uint16_t x,uint16_t y,uint16_t color)
{
uint8_t cloud_pixel[5*11]=
{
0b00111110,0b01000001,0b01000001,0b01000001,0b00100010, // C
0b00000000,0b00000000,0b01111111,0b00000000,0b00000000, // l
0b00001110,0b00010001,0b00010001,0b00010001,0b00001110, // o
0b00011110,0b00000001,0b00000001,0b00000001,0b00011111, // u
0b00001110,0b00010001,0b00010001,0b00010001,0b01111111, // d
0b00000000,0b00000000,0b00000000,0b00000000,0b00000000, // space
0b01111111,0b01001000,0b01001000,0b01001000,0b00110000, // P
0b00000000,0b00000000,0b01011111,0b00000000,0b00000000, // i
0b00010001,0b00001010,0b00000100,0b00001010,0b00010001, // x
0b00001110,0b00010101,0b00010101,0b00010101,0b00001100, // e
0b00000000,0b00000000,0b01111111,0b00000000,0b00000000 // l
};
uint16_t _x = x - (5*5*5) - 6;
uint16_t _y = y - 20;
for(uint8_t i=0;i<11;i++)
{
if(i == 1 || i == 2 || i ==5 || i==6 ||i==7 ||i==8 || i == 10)
{
_x = _x -6;
}
else
{
_x = _x+4;
}
for(uint8_t m=0;m<5;m++)
{
_x = _x +5;
_y = y - 20;
for(uint8_t n=0;n<8;n++)
{
if((cloud_pixel[i*5+m]>>(7-n))&0x01)
{
tft->fillRect(_x+1,_y+1,4,4,color);
}
_y += 5;
}
}
}
}
- 4.2 Display the "todoist" logo in the first interface on the screen.
void display_todoist_icon(uint8_t PNGnum)
{
char szTemp[256];
int rc;
if(PNGnum == 1 )
{
rc = png.openFLASH((uint8_t *)TodoistIcon, sizeof(TodoistIcon), PNGDraw);
}
else if(PNGnum == 2)
{
rc = png.openFLASH((uint8_t *)TodoistIcon_mini, sizeof(TodoistIcon_mini), PNGDraw);
}
if (rc == PNG_SUCCESS) {
rc = png.decode(NULL, 0); // no private structure and skip CRC checking
png.close();
}
}
- 4.3 Display the "WiFi Signal" in the first interface on the screen.
when not connect to the WiFi network.
when connected to the WiFi network.
void display_wifi_status(uint8_t x,uint8_t y)
{
if( status != WL_CONNECTED)
{
tft->fillCircle(x,y,3,DARKGREY);
tft->fillArc(x,y, 5, 7, 225, 315, DARKGREY);
tft->fillArc(x,y, 9, 11, 225, 315, DARKGREY);
tft->fillArc(x,y, 13, 15, 225, 315, DARKGREY);
}
else
{
tft->fillCircle(x,y,3,GREEN);
tft->fillArc(x,y, 5, 7, 225, 315, GREEN);
tft->fillArc(x,y, 9, 11, 225, 315, GREEN);
tft->fillArc(x,y, 13, 15, 225, 315, GREEN);
}
}
- 4.4 Display the dashboard in the “void setup()”
void display_dashboard()
{
tft->fillScreen(WHITE);
image_x = 6;
image_y = 0;
display_todoist_icon(2);
tft->setTextColor(DARKGREEN);
tft->setTextSize(2);
tft->setCursor(150, 1);
tft->print(String("Todolist Sync Monitor"));
tft->drawLine(20,18,480,18,RED);
}
- 4.4 Display the todolist “Project”“Section”and“tasks”;
case display_todolist:
{
uint8_t display_row_num = 0;
uint8_t display_page_num = 0;
uint8_t display_string_len = 0;
String display_string;
display_task_num = 0;
display_dashboard();
for(uint8_t i=0; i<todoist_project_num; i++)
{
if(jump_out_flag)
{
jump_out_flag =0;
return;
}
#ifdef debug_msg
Serial.print("todoist_project_num:");
Serial.println(i);
#endif
if(todoist_project[i].Exist)
{
tft->fillCircle(12,26+(20*display_row_num),5,Str2RGB565(todoist_project[i].Color_Str));
tft->setTextColor(Str2RGB565(todoist_project[i].Color_Str));
tft->setTextSize(2);
display_string_len = todoist_project[i].Name.length();
if(display_string_len > 38)
{
while(display_string_len > 38)
{
display_string = todoist_project[i].Name.substring(todoist_project[i].Name.length()- display_string_len, todoist_project[i].Name.length()-display_string_len+38);
display_string_len -= 38;
tft->setCursor(24, 20+(20*display_row_num));
tft->print(String("[") + display_string + String("]"));
display_row_num++;
}
display_string = todoist_project[i].Name.substring(todoist_project[i].Name.length()-display_string_len, todoist_project[i].Name.length());
tft->setCursor(24, 20+(20*display_row_num));
tft->print(String("[") + display_string + String("]"));
display_row_num++;
}
else
{
tft->setCursor(24, 20+(20*display_row_num));
tft->print(String("[") + todoist_project[i].Name + String("]"));
display_row_num++;
}
for(uint8_t m=0; m<todoist_sections_num; m++)
{
if(jump_out_flag)
{
jump_out_flag =0;
return;
}
if(todoist_sections[m].Exist)
{
if(todoist_sections[m].Project == todoist_project[i].ID)
{
#ifdef debug_msg
Serial.print("todoist_sections_num:");
Serial.println(m);
#endif
display_string_len = todoist_sections[m].Name.length();
if(display_string_len > 38)
{
while(display_string_len > 38)
{
display_string = todoist_sections[m].Name.substring(todoist_sections[m].Name.length()- display_string_len, todoist_sections[m].Name.length()-display_string_len+38);
display_string_len -= 38;
tft->setCursor(24, 20+(20*display_row_num));
tft->print(display_string);
display_row_num++;
if(display_row_num == 15)
{
display_row_num =0;
wait_task_finish_click();display_dashboard();
}
}
display_string = todoist_sections[m].Name.substring(todoist_sections[m].Name.length()- display_string_len, todoist_sections[m].Name.length());
tft->setCursor(24, 20+(20*display_row_num));
tft->print(display_string);
display_row_num++;
if(display_row_num == 15)
{
display_row_num =0;
wait_task_finish_click();display_dashboard();
}
}
else
{
tft->setCursor(24, 20+(20*display_row_num));
tft->print(todoist_sections[m].Name);
display_row_num++;
if(display_row_num == 15)
{
display_row_num =0;
wait_task_finish_click();display_dashboard();
}
}
tft->setTextColor(DARKGREY);
for(uint8_t n=0; n<todoist_task_num; n++)
{
if(jump_out_flag)
{
jump_out_flag =0;
return;
}
if(todoist_task[n].Exist)
{
if((todoist_task[n].Section == todoist_sections[m].ID)&& (todoist_task[n].Parent == "ul"))
{
#ifdef debug_msg
// Serial.print("todoist_task_num:");
// Serial.println(n);
// Serial.print("todoist_task_ID:");
// Serial.println(todoist_task[n].ID);
// Serial.print("todoist_task_Name:");
// Serial.println(todoist_task[n].Name);
#endif
tft->drawCircle(36,26+(20*display_row_num),5,Str2RGB565(todoist_project[i].Color_Str));
todoist_task_select[display_task_num].x = 36;
todoist_task_select[display_task_num].y = 26+(20*display_row_num);
todoist_task_select[display_task_num].ID = todoist_task[n].ID;
todoist_task_select[display_task_num].Name = todoist_task[n].Name;
display_task_num++;
if(todoist_task[n].Finished)
{
tft->fillCircle(36,26+(20*display_row_num),3,Str2RGB565(todoist_project[i].Color_Str));
tft->drawLine(48,26+(20*display_row_num),48+todoist_task[n].Name.length()*12,26+(20*display_row_num),Str2RGB565(todoist_project[i].Color_Str));
}
display_string_len = todoist_task[n].Name.length();
if(display_string_len > 36)
{
while(display_string_len > 36)
{
display_string = todoist_task[n].Name.substring(todoist_task[n].Name.length()- display_string_len, todoist_task[n].Name.length()-display_string_len+36);
display_string_len -= 36;
tft->setCursor(48, 20+(20*display_row_num));
tft->print(display_string);
display_row_num++;
if(display_row_num == 15)
{
display_row_num =0;
wait_task_finish_click();display_dashboard();
}
}
display_string = todoist_task[n].Name.substring(todoist_task[n].Name.length()-display_string_len, todoist_task[n].Name.length());
tft->setCursor(48, 20+(20*display_row_num));
tft->print(display_string);
display_row_num++;
if(display_row_num == 15)
{
display_row_num =0;
wait_task_finish_click();display_dashboard();
}
}
else
{
tft->setCursor(48, 20+(20*display_row_num));
tft->print(todoist_task[n].Name);
display_row_num++;
if(display_row_num == 15)
{
display_row_num =0;
wait_task_finish_click();display_dashboard();
}
}
for(uint8_t p=n; p<todoist_task_num; p++)
{
if(jump_out_flag)
{
jump_out_flag =0;
return;
}
if((todoist_task[p].Parent == todoist_task[n].ID))
{
#ifdef debug_msg
// Serial.print("todoist_task_num:");
// Serial.println(p);
// Serial.print("todoist_task_ID:");
// Serial.println(todoist_task[p].ID);
// Serial.print("todoist_task_Parent:");
// Serial.println(todoist_task[p].Parent);
// Serial.print("todoist_task.ID");
// Serial.print(n);
// Serial.print(":");
// Serial.println(todoist_task[n].ID);
#endif
tft->drawCircle(60,26+(20*display_row_num),5,Str2RGB565(todoist_project[i].Color_Str));
todoist_task_select[display_task_num].x = 60;
todoist_task_select[display_task_num].y = 26+(20*display_row_num);
todoist_task_select[display_task_num].ID = todoist_task[p].ID;
todoist_task_select[display_task_num].Name = todoist_task[p].Name;
display_task_num++;
if(todoist_task[p].Finished)
{
tft->fillCircle(60,26+(20*display_row_num),3,Str2RGB565(todoist_project[i].Color_Str));
tft->drawLine(72,26+(20*display_row_num),72+todoist_task[p].Name.length()*12,26+(20*display_row_num),Str2RGB565(todoist_project[i].Color_Str));
}
display_string_len = todoist_task[p].Name.length();
if(display_string_len > 34)
{
while(display_string_len > 34)
{
display_string = todoist_task[p].Name.substring(todoist_task[p].Name.length()- display_string_len, todoist_task[p].Name.length()-display_string_len+34);
display_string_len -= 34;
tft->setCursor(72, 20+(20*display_row_num));
tft->print(display_string);
display_row_num++;
if(display_row_num == 15)
{
display_row_num =0;
wait_task_finish_click();display_dashboard();
}
}
display_string = todoist_task[p].Name.substring(todoist_task[p].Name.length()-display_string_len, todoist_task[p].Name.length());
tft->setCursor(72, 20+(20*display_row_num));
tft->print(display_string);
display_row_num++;
}
else
{
tft->setCursor(72, 20+(20*display_row_num));
tft->print(todoist_task[p].Name);
display_row_num++;
}
if(display_row_num == 15)
{
display_row_num =0;
wait_task_finish_click();display_dashboard();
}
for(uint8_t q=0; q<todoist_task_num; q++)
{
if(jump_out_flag)
{
jump_out_flag =0;
return;
}
if((todoist_task[q].Parent == todoist_task[p].ID))
{
#ifdef debug_msg
// Serial.print("todoist_task_num2:");
// Serial.println(q);
// Serial.print("todoist_task_ID2:");
// Serial.println(todoist_task[q].ID);
// Serial.print("todoist_task_Parent2:");
// Serial.println(todoist_task[q].Parent);
#endif
tft->drawCircle(84,26+(20*display_row_num),5,Str2RGB565(todoist_project[i].Color_Str));
todoist_task_select[display_task_num].x = 84;
todoist_task_select[display_task_num].y = 26+(20*display_row_num);
todoist_task_select[display_task_num].ID = todoist_task[q].ID;
todoist_task_select[display_task_num].Name = todoist_task[q].Name;
display_task_num++;
if(todoist_task[q].Finished)
{
tft->fillCircle(84,26+(20*display_row_num),3,Str2RGB565(todoist_project[i].Color_Str));
tft->drawLine(96,26+(20*display_row_num),96+todoist_task[q].Name.length()*12,26+(20*display_row_num),Str2RGB565(todoist_project[i].Color_Str));
}
display_string_len = todoist_task[q].Name.length();
if(display_string_len > 32)
{
while(display_string_len > 32)
{
display_string = todoist_task[q].Name.substring(todoist_task[q].Name.length()- display_string_len, todoist_task[q].Name.length()-display_string_len+32);
display_string_len -= 32;
tft->setCursor(96, 20+(20*display_row_num));
tft->print(display_string);
display_row_num++;
if(display_row_num == 15)
{
display_row_num =0;
wait_task_finish_click();display_dashboard();
}
}
display_string = todoist_task[q].Name.substring(todoist_task[q].Name.length()-display_string_len, todoist_task[q].Name.length());
tft->setCursor(96, 20+(20*display_row_num));
tft->print(display_string);
display_row_num++;
}
else
{
tft->setCursor(96, 20+(20*display_row_num));
tft->print(todoist_task[q].Name);
display_row_num++;
}
if(display_row_num == 15)
{
display_row_num =0;
wait_task_finish_click();display_dashboard();
}
}
}
}
}
}
}
}
}
}
}
tft->setTextColor(DARKGREY);
for(uint8_t n=0; n<todoist_task_num;n++)
{
if(jump_out_flag)
{
jump_out_flag =0;
return;
}
if(todoist_task[n].Parent == todoist_project[i].ID)
{
#ifdef debug_msg
Serial.print("todoist_task_num:");
Serial.println(n);
#endif
tft->drawCircle(48,26+(20*display_row_num),5,Str2RGB565(todoist_project[i].Color_Str));
todoist_task_select[display_task_num].x = 48;
todoist_task_select[display_task_num].y = 26+(20*display_row_num);
todoist_task_select[display_task_num].ID = todoist_task[n].ID;
todoist_task_select[display_task_num].Name = todoist_task[n].Name;
display_task_num++;
display_string_len = todoist_task[n].Name.length();
if(display_string_len > 34)
{
while(display_string_len > 34)
{
display_string = todoist_task[n].Name.substring(todoist_task[n].Name.length()- display_string_len, todoist_task[n].Name.length()-display_string_len+34);
display_string_len -= 34;
tft->setCursor(24, 20+(20*display_row_num));
tft->print(display_string);
display_row_num++;
if(display_row_num == 15)
{
display_row_num =0;
wait_task_finish_click();display_dashboard();
}
}
display_string = todoist_task[n].Name.substring(todoist_task[n].Name.length()-display_string_len, todoist_task[n].Name.length());
tft->setCursor(24, 20+(20*display_row_num));
tft->print(display_string);
display_row_num++;
}
else
{
tft->setCursor(48, 20+(20*display_row_num));
tft->print(todoist_task[n].Name);
display_row_num++;
}
if(display_row_num == 15)
{
display_row_num =0;
wait_task_finish_click();
display_dashboard();
}
}
}
}
else
{
return;
}
}
wait_task_finish_click();
if(display_time_cnt == 6)
{
display_time_cnt = 0;
display_dashboard();
tft->setTextColor(DARKGREEN);
tft->setTextSize(2);
tft->setCursor(132, 120);
tft->print("Get Todolist Again");
currentState = send_project_request;
}
else
{
display_time_cnt++;
}
//currentState = todolist_close_task;
}
break;
The interface for displaying tasks may require multiple interfaces to be displayed alternately, and each interface stays for 10 seconds. When the button is pressed, it can switch between tasks and reset the timer; and the last task beyond this interface will be automatically refreshed as the next task interface.The interface for displaying tasks may require multiple interfaces to be displayed alternately, and each interface stays for 10 seconds. When the button is pressed, it can switch between tasks and reset the timer; and the last task beyond this interface will be automatically refreshed as the next task interface. After displaying all the interfaces 6 times in a loop, jump to case "send_project_request" to get task list information from the server again.
This is the processing flow of the button. Pressing the button can switch between tasks.
void wait_task_finish_click()
{
uint8_t task_select = 0;
for(uint16_t time_cnt; time_cnt <10000; time_cnt++)
{
if(buttonState)
{
time_cnt = 0;
delay(150);
if(buttonState)
{
for(uint16_t time_cnt2 = 0; time_cnt2 <1000; time_cnt2++)
{
if(!buttonState)
{
if(task_select > 0 && task_select < display_task_num)
{
tft->fillCircle(todoist_task_select[task_select-1].x,todoist_task_select[task_select-1].y,3,WHITE);
tft->fillCircle(todoist_task_select[task_select].x,todoist_task_select[task_select].y,3,BLACK);
}
else if(task_select < display_task_num)
{
tft->fillCircle(todoist_task_select[task_select].x,todoist_task_select[task_select].y,3,BLACK);
}
else if(task_select == display_task_num)
{
task_select = 0;
display_task_num=0;
return;
}
task_select++;
time_cnt2 = 1000;
}
else
{
delay(1);
}
}
if(buttonState)
{
tft->drawLine(todoist_task_select[task_select-1].x+12,todoist_task_select[task_select-1].y,todoist_task_select[task_select-1].x+todoist_task_select[task_select-1].Name.length()*12,todoist_task_select[task_select-1].y,BLACK);
delay(500);
todoist_task_close.ID = todoist_task_select[task_select-1].ID;
todoist_task_close.Name = todoist_task_select[task_select-1].Name;
currentState = todolist_close_task;
jump_out_flag = 1;
return;
}
}
}
else
{
delay(1);
}
}
display_task_num=0;
}
Step 5: Finish the task on Todolist Sync Monitor.When the task is completed, the task can be closed by long-press the button.
case todolist_close_task:
{
if(!display_close_int)
{
//currentState = send_project_request;
display_dashboard();
tft->drawRect(50,70,385,150,BLACK);
tft->setTextColor(BLACK);
tft->setTextSize(2);
tft->setCursor(60, 80);
tft->print(String("Do you want to close this task:"));
tft->setTextColor(DARKGREY);
tft->setTextSize(2);
tft->setCursor((480-(12*todoist_task_close.Name.length()))/2, 120);
tft->print(todoist_task_close.Name);
tft->fillRect(160,175,46,25,WHITE);
tft->drawRect(160,175,46,25,RED);
tft->setTextColor(RED);
tft->setTextSize(2);
tft->setCursor(165, 180);
tft->print("YES");
tft->fillRect(280,175,40,25,WHITE);
tft->drawRect(280,175,40,25,GREEN);
tft->setTextColor(GREEN);
tft->setTextSize(2);
tft->setCursor(288, 180);
tft->print("NO");
display_close_int = true;
}
else
{
if(buttonState)
{
delay(150);
if(buttonState)
{
yes_or_no =!yes_or_no;
for(uint16_t time_cnt = 0; time_cnt <1000; time_cnt++)
{
if(!buttonState)
{
if(yes_or_no)
{
tft->fillRect(160,175,46,25,WHITE);
tft->drawRect(160,175,46,25,RED);
tft->setTextColor(RED);
tft->setTextSize(2);
tft->setCursor(165, 180);
tft->print("YES");
tft->fillRect(280,175,40,25,GREEN);
tft->setTextColor(WHITE);
tft->setTextSize(2);
tft->setCursor(288, 180);
tft->print("NO");
}
else
{
tft->fillRect(160,175,46,25,RED);
tft->setTextColor(WHITE);
tft->setTextSize(2);
tft->setCursor(165, 180);
tft->print("YES");
tft->fillRect(280,175,40,25,WHITE);
tft->drawRect(280,175,40,25,GREEN);
tft->setTextColor(GREEN);
tft->setTextSize(2);
tft->setCursor(288, 180);
tft->print("NO");
}
time_cnt = 1000;
}
else
{
delay(1);
}
if(time_cnt == 999)
{
display_dashboard();
if(yes_or_no)
{
tft->setTextColor(RED);
tft->setTextSize(2);
tft->setCursor(180, 120);
tft->print("Task Closing");
yes_or_no = false;
if (client.connectSSL(todoist_server,443)) {
delay(3000);
// Make a HTTP request
client.println(String("POST /rest/v2/tasks/")+todoist_task_close.ID+ String("/close HTTP/1.1"));
client.println(String("Host: ") + todoist_server);
client.println(String("Authorization: Bearer ") + String(todoist_token));
client.println("Connection: close");
client.println();
json_String= "";
currentState = show_close_result;
}
}
else
{
currentState = display_todolist;
}
display_close_int = false;
}
}
}
}
}
}
break;
case show_close_result:
{
while (client.available()) {
json_String += (char)client.read();
data_now = 1;
dataEnd = 0;
}
if(data_now)
{
#ifdef debug_msg
Serial.println(json_String);
#endif
client.stop();
delay(2000);
currentState = send_project_request;
}
}
break;
}
}
Take closing this task"Todoist API Communication" as an example, the interface is displayed as follows.
- 5.1 when select the task:
- 5.2 long-press the button to close the task.
- 5.3 make sure to close the task"Todoist API Communication".
- 5.4 press the button the select "YES" or "NO", NO to return the task display.
- 5.5 long-press the button to close the task when select "YES".
- 5.6 close the task.
Close a task:
Close a task:
$ curl -X POST "https://api.todoist.com/rest/v2/tasks/2995104339/close" \
-H "Authorization: Bearer $token"
The API returns an empty response with status 204. SDK clients will respond with true
to indicate success.
The API returns an empty response with status 204. SDK clients will respond with true
to indicate success.
When the task is successfully closed, the task is closed in the newly obtained list.
On the "Todoist" website, you can see that the corresponding task has been closed.
Done!
Comments