This commit is contained in:
Roelandts_Gert
2023-11-27 14:49:26 +01:00
12 changed files with 1146 additions and 11 deletions

View File

@@ -67,4 +67,5 @@ This folder contains the following documents:
- [logger.md](docs/logger.md): Logging and Debugging Messages
- [mkllfs.md](docs/mkllfs.md): Make Linked List File System
- [style_guide.md](docs/style_guide.md): Style Guide
- [tftp.md](docs/tftp.md): Trivial File Transfer Protocol
- [tftp.md](docs/tftp.md): Trivial File Transfer Protocol
- [udp_broadcast.md](docs/udp_broadcast.md): UDP Broadcast

View File

@@ -104,7 +104,7 @@ void main(void) {
#### Drawing text on the screen
```c
void lcd_display_text(const char* text, uint16_t x_pos, uint16_t y_pos, uint32_t color, sFONT *font);
void lcd_display_text(const char* text, uint16_t x_pos, uint16_t y_pos, uint32_t color, uint32_t bg_color, sFONT *font);
```
```c
@@ -116,7 +116,7 @@ void main(void) {
...
lcd_init(true);
...
lcd_display_text("This is a text string.", 10, 10, LCD_GREEN, LCD_FONT16);
lcd_display_text("This is a text string.", 10, 10, LCD_GREEN, LCD_BLACK, LCD_FONT16);
}
```
Display text on the LCD screen in a certain color. When text width exceeds BSP_LCD_GetXSize(), a text wrap will be performed. If the text wrap is between two will be injected.
@@ -279,3 +279,20 @@ void main(void) {
```
Clears all text strings on the LCD screen.
#### Clearing (background/images) layer 0 to color
```c
void lcd_set_bg_color_layer0(uint32_t color);
```
```c
#include "lcd_api.h"
void main(void) {
...
lcd_init(true);
lcd_draw_img_from_fs("st.bmp", 300, 100);
...
lcd_set_bg_color_layer0(LCD_BLACK);
}
```

View File

@@ -1,5 +1,14 @@
# TFTP
This is the documentation of the TFTP task
## Table of contents
- [Table of contents](#table-of-contents)
- [Initialization](#initialization)
- [Deinitialization](#deinitialization)
- [Usage](#usage)
- [Receive a file](#receive-a-file)
- [Send a file](#send-a-file)
## Initialization
The TFTP task is initialized in the main function.
```c

116
docs/udp_broadcast.md Normal file
View File

@@ -0,0 +1,116 @@
# UDP broadcast
## Introduction
The UDP broadcast code is used to handle incoming UDP datagrams.
there are currently 2 types of datagrams it processes:
- broadcasts: "Where are you?v1.0", replies with current owner details.
- change of details: "func1:name: ..., surname: ...", replies with changed owner details.
It also writes the current owner's name on the screen and updates it everytime it's changed.
## Table of contents
- [Introduction](#introduction)
- [Table of contents](#table-of-contents)
- [Usage of UDP broadcast](#usage-of-udp-broadcast)
- [Initialization of UDP broadcast](#initialization-of-udp-broadcast)
- [Initialization of UDP connection](#initialization-of-udp-connection)
- [Owner details interface](#owner-details-interface)
- [Setting owner details](#setting-owner-details)
- [Getting owner details](#getting-owner-details)
## Usage of UDP broadcast
### Initialization of UDP broadcast
The 'udp_broadcast_init(uint16_t x_pos, uint16_t y_pos)' function does 4 things:
1. It initializes the coördinates of where the owner's name has to be written on the LCD.
2. [It initializes the UDP connection](#initialization-of-udp-connection)
3. It initializes the owner's details with a default name.
4. Returns the error value of [udp_broadcast_connection_init()](#initialization-of-udp-connection). This way the user can use some code to check whether the "connection" was initialized correctly.
```c
#include "UDP_broadcast.h'
...
void main(void){
...
if (udp_broadcast_init(270,255) != ERR_OK){
...
}
...
}
```
### Initialization of UDP connection
The 'udp_broadcast_connection_init()' funciton does 2 things:
1. Initializes the UDP "connection" so that incoming datagrams can be processed and replied to. It binds to port 64000 and listens to every IP-address in the local network.
2. returns the LWIP error code so that [err_t udp_broadcast_init(uint16_t x_pos, uint16_t y_pos)](#initialization-of-udp-broadcast) knows the "connection" is initializes correctly
This function can be used seperately from [err_t udp_broadcast_init(uint16_t x_pos, uint16_t y_pos)](#initialization-of-udp-broadcast), this gives the possibility to try "connecting" again after the first time failed.
```c
#include "UDP_broadcast.h'
...
void main(void){
...
if (udp_broadcast_init(10,255) == ERR_OK){
goto connected;
}
LOG_WARN(TAG,"error initializing udp connection, trying again in 500ms");
HAL_Delay(500);
if(udp_broadcast_connection_init() != ERR_OK){
LOG_WARN(TAG,"error initializing udp connection, check warnings from udp_broadcast_init() or udp_broadcast_connection_init()");
}
connected:
...
}
```
### Owner details interface
The interface to ask for the owner's details and change them is a modified version of the [Qt application](https://github.com/wimdams/Device_finder) Wim Dams build. His only has the functionality to ask for the owner's details.
Just because the owner's details might want to be used in other code, some functions have been written for obtaining these in the STM32 code aswell.
#### Setting owner details
THe 'udp_broadcast_set_owner_details(const char* , const char*)' function does 2 things:
1. Set the owner details, the order of the parameters is: name, surname
2. Return 1 if the owner's details have been set correctly and 0 if not.
```c
#include "UDP_broadcast.h'
...
void main(void){
...
if (udp_broadcast_set_owner_details("Joran", "Van Nieuwenhoven") != ERR_OK){
...
}
...
}
```
#### Getting owner details
There are 3 functions:
- udp_broadcast_get_owner_details_name(): returns the owner's name.
- udp_broadcast_get_owner_details_surname(): returns the owner's surname.
- udp_broadcast_get_owner_details_reply(): returns what would be replied to a UDP broadcast with datagram "Where are you?v1.0".
```c
#include <string.h>
#include "UDP_broadcast.h'
...
void main(void){
...
char name[20];
char surname[20];
char reply[120];
strncp(name, udp_broadcast_get_owner_details_name(), sizeof(name) - 1);
strncp(surname, udp_broadcast_get_owner_details_surname(), sizeof(surname) - 1);
strncp(reply, udp_broadcast_get_owner_details_reply(), sizeof(reply) - 1);
...
}
```

View File

@@ -0,0 +1,127 @@
/**
* @file UDP_broadcast.h
*
* @brief UDP broadcast handler
* Created on: Nov 6, 2023
* Author: joran
*/
#ifndef INC_UDP_BROADCAST_H_
#define INC_UDP_BROADCAST_H_
// includes
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "lwip.h"
#include "lwip/netif.h"
#include "udp.h"
#include "lcd_api.h"
// Defines used by UDP callback
#define UDP_BROADCAST_MAX_DATA_SIZE ((UDP_BROADCAST_MAX_NAME_SIZE * 2) - 2 + 25) // Define the maximum expected data size
#define UDP_BROADCAST_UDP_QUESTION1 "Where are you?v1.0" // Expected question from UDP client
#define UDP_BROADCAST_MAX_FUNC_LEN 7
#define UDP_BROADCAST_MAX_COLON_COMMA_COUNT 4
// Defines used by owner details
#define UDP_BROADCAST_MAX_REPLY_SIZE (UDP_BROADCAST_MAX_MAC_ADDR_LEN + sizeof(UDP_BROADCAST_REPLY_MIDDLE_TEXT) + (UDP_BROADCAST_MAX_NAME_SIZE * 2) + UDP_BROADCAST_MAX_REPLY_SIZE_EXTRA)
#define UDP_BROADCAST_REPLY_MIDDLE_TEXT "is present and my owner is"
#define UDP_BROADCAST_MAX_MAC_ADDR_LEN 19 // Format is: "xx:xx:xx:xx:xx:xx"
#define UDP_BROADCAST_MAX_NAME_SIZE 21 // Code automatically leaves 1 char for '\0' (actual length = length - 1)
#define UDP_BROADCAST_MAX_REPLY_SIZE_EXTRA 20 // Just a bit extra
#define UDP_BROADCAST_LCD_NAME_PRE_TEXT "New owner: "
#define UDP_BROADCAST_LCD_TEXT_SIZE (strlen(UDP_BROADCAST_LCD_NAME_PRE_TEXT) + UDP_BROADCAST_MAX_NAME_SIZE)
/**
* @struct owner_details_t
* @brief contains information about the owner
*
*/
typedef struct {
char name[UDP_BROADCAST_MAX_NAME_SIZE];
char surname[UDP_BROADCAST_MAX_NAME_SIZE];
uint8_t mac_address[6];
char reply[UDP_BROADCAST_MAX_REPLY_SIZE];
} owner_details_t;
// The following functions are used for owner details (those that must be available in main)
/**
* @fn err_t udp_broadcast_set_owner_details(const char*, const char*)
* @brief udp_broadcast_set_owner_details() is the interface that can be used in other files
* to set the owner's details
*
* @param[in] name string containing the new owner's name
* @param[in] surname string containing the new owner's surname
* @return lwIP error code.
* - ERR_OK. Successful. No error occurred.
* - ERR_ARG. one or both arguments are NULL pointers
*/
err_t udp_broadcast_set_owner_details(const char*, const char*);
/**
* @fn char udp_broadcast_get_owner_details_name*(void)
* @brief udp_broadcast_get_owner_details_name() can be used to get the current owner's name
*
* @return name of owner
* this name is set by @see udp_broadcast_set_owner_details_name()
*/
char* udp_broadcast_get_owner_details_name(void);
/**
* @fn char udp_broadcast_get_owner_details_surname*(void)
* @brief udp_broadcast_get_owner_details_surname() can be used to get the current owner's surname
*
* @return surname of owner
* this name is set by @see udp_broadcast_set_owner_details_surname()
*/
char* udp_broadcast_get_owner_details_surname(void);
/**
* @fn char udp_broadcast_get_owner_details_reply*(void)
* @brief udp_broadcast_get_owner_details_reply() can be used to get the current UDP reply
*
* @return reply for UDP broadcast
* this reply is formatted by @see format_reply()
*/
char* udp_broadcast_get_owner_details_reply(void);
// Initialization functions
/**
* @fn err_t udp_broadcast_init(uint16_t x_pos, uint16_t y_pos)
* @brief udp_broadcast_init() initializes the owner's variables and calls upon @see udp_broadcast_connection_init()
*
* @param[in] x_pos : uint16_t that sets the x coordinate the owner's name will be written on the LCD
* @param[in] y_pos : uint16_t that sets the y coordinate the owner's name will be written on the LCD
* @return lwIP error code.
* - ERR_OK. Successful. No error occurred.
* - ERR_USE. The specified ipaddr and port are already bound to by another UDP PCB.
* - ERR_MEM. udp pcb couldn't be created
*
* - ERR_ARG. one or both arguments of udp_broadcast_set_owner_details() are NULL pointers
*/
err_t udp_broadcast_init(uint16_t x_pos, uint16_t y_pos);
/**
* @fn err_t udp_broadcast_connection_init()
* @brief udp_broadcast_connection_init() initializes the UDP connection so that it listens for all traffic on
* port 6400
* it is called by @see udp_broadcast_init() aswell but can be used separately if it failed before
*
* @return lwIP error code.
* - ERR_OK. Successful. No error occurred.
* - ERR_USE. The specified ipaddr and port are already bound to by another UDP PCB.
* - ERR_MEM. udp pcb couldn't be created
*/
err_t udp_broadcast_connection_init(void);
#endif /* INC_UDP_BROADCAST_H_ */

View File

@@ -96,13 +96,16 @@ void lcd_task(void);
* a text wrap will be performed. If the text wrap is between two letters in a word, the '-' character
* will be injected.
*
* @note When bg_color 0 is passed as a parameter, the background for the text will be transparent
*
* @param[in] text C-style text string to display on the LCD screen
* @param[in] x_pos X-position
* @param[in] y_pos Y-position
* @param[in] color Color in which the text will be displayed, see preset colors in defines above
* @param[in] bg_color Text background color
* @param[in] font Font size, see defines above in file
*/
void lcd_display_text(const char* text, uint16_t x_pos, uint16_t y_pos, uint32_t color, sFONT *font);
void lcd_display_text(const char* text, uint16_t x_pos, uint16_t y_pos, uint32_t color, uint32_t bg_color, sFONT *font);
/**
* @brief Draw BMP image on screen
@@ -156,6 +159,14 @@ void lcd_clear_text(void);
*/
void lcd_clear_images(void);
/**
* @brief Set background color of layer 0
* Sets the layer 0 color
*
* @param[in] color Color of layer 0 (images layer)
*/
void lcd_set_bg_color_layer0(uint32_t color);
/**
* @brief LCD stop all GIFs
* Stops all playing GIFs on lcd screen

View File

@@ -0,0 +1,19 @@
/**
* @file mqtt_application.h
* @brief header for mosquitto application of the groups assignment
* @author RobinVdB
*/
#ifndef INC_MQTTA_H_
#define INC_MQTTA_H_
#include <stdint.h>
/**
* @brief Initialize MQTT application
*
* @output returns 1 if the init failed to create a client and start an MQTT connection
*/
uint8_t mqtt_application_init(void);
#endif /* INC_MQTTA_H_ */

View File

@@ -0,0 +1,399 @@
/**
* @file UDP_broadcast.c
*
* @brief UDP broadcast handler
* Created on: Nov 6, 2023
* Author: joran
*/
// Includes
#include "UDP_broadcast.h"
#define LOGGER_LEVEL_INFO
#include "log.h"
// Global variables
static const char* TAG = "UDP_broadcast"; // Tag used in logs
static owner_details_t udp_owner;
static uint16_t owner_name_x_pos;
static uint16_t owner_name_y_pos;
// Functions
static void udp_broadcast_set_owner_details_mac(void);
static void udp_broadcast_name_to_lcd(void);
static uint8_t udp_broadcast_set_owner_details_name(const char* name);
static uint8_t udp_broadcast_set_owner_details_surname(const char* surname);
static uint8_t udp_broadcast_set_owner_details_reply(const char* reply);
static void udp_broadcast_format_reply(void);
static void udp_receive_callback(void* arg,
struct udp_pcb* connection,
struct pbuf* p,
const ip_addr_t* addr,
u16_t port);
/**
* @fn uint8_t udp_broadcast_set_owner_details_mac(owner_details_t*)
* @brief set_owner_details_mac() gets the MAC address from the default netif
* and sets it in the owner_details_t struct
*/
static void udp_broadcast_set_owner_details_mac(void) {
for (uint8_t i = 0; i < 6; i++) {
udp_owner.mac_address[i] = netif_default->hwaddr[i]; // Access the MAC address
}
}
/**
* @fn void udp_broadcast_name_to_lcd(void)
* @brief prints the owner's name with
* @see UDP_BROADCAST_LCD_NAME_PRE_TEXT in front of it
*/
static void udp_broadcast_name_to_lcd(void){
char text[UDP_BROADCAST_LCD_TEXT_SIZE];
memset(text,' ',UDP_BROADCAST_LCD_TEXT_SIZE); // Fill with spaces
text[UDP_BROADCAST_LCD_TEXT_SIZE - 1] = '\0'; // Make the last a NULL byte
lcd_display_text(text, owner_name_x_pos, owner_name_y_pos, LCD_BLACK, LCD_WHITE, LCD_FONT12);
snprintf(text, UDP_BROADCAST_LCD_TEXT_SIZE, "%s%s",UDP_BROADCAST_LCD_NAME_PRE_TEXT,
udp_owner.name);
lcd_display_text(text, owner_name_x_pos, owner_name_y_pos, LCD_BLACK, LCD_WHITE, LCD_FONT12);
}
/**
* @fn uint8_t udp_broadcast_set_owner_details_name(owner_details_t*, const char*)
* @brief set_owner_details_name() sets the owner's name in the owner_details_t struct
* strncpy is used to copy the function paremeter safely to the owner_details_t's name
* it also uses the lcd api to display the latest owner's name
*
* @param[in] name string containing the owner's name
* @return setting owner name error
* - 0: no error occured, name was set
* - 1: an error occured, name pointer is NULL
*/
static uint8_t udp_broadcast_set_owner_details_name(const char* name) {
if (name == NULL) {
LOG_WARN(TAG, "%s: string given is a NULL pointer", __func__);
return 1;
}
LOG_DEBUG(TAG, "set: %s", name);
strncpy(udp_owner.name, name, sizeof(udp_owner.name) - 1); // -1: compensate for '\0'
udp_broadcast_name_to_lcd();
return 0;
}
/**
* @fn uint8_t udp_broadcast_set_owner_details_surname(owner_details_t*, const char*)
* @brief set_owner_details_surname() sets the owner's surname in the owner_details_t struct
* strncpy is used to copy the function paremeter safely to the owner_details_t's surname
*
* @param[in] surname string containing the owner's surname
* @return setting owner surname error
* - 0: no error occured, surname was set
* - 1: an error occured, surname pointer is NULL
*/
static uint8_t udp_broadcast_set_owner_details_surname(const char* surname) {
if (surname == NULL) {
LOG_WARN(TAG, "%s: string given is a NULL pointer", __func__);
return 1;
}
LOG_DEBUG(TAG, "set: %s", surname);
strncpy(udp_owner.surname, surname, sizeof(udp_owner.surname)-1); // -1: compensate for '\0'
return 0;
}
/**
* @fn uint8_t udp_broadcast_set_owner_details_reply(const char*)
* @brief set_owner_details_reply() sets the UDP reply in the owner_details_t struct
* strncpy is used to copy the function paremeter safely to the owner_details_t's reply
*
* @param[in] reply string used to reply to the UDP broadcast
* @return setting owner reply error
* - 0: no error occured, reply was set
* - 1: an error occured, reply pointer is null
*/
static uint8_t udp_broadcast_set_owner_details_reply(const char* reply) {
if (reply == NULL) {
LOG_WARN(TAG, "%s: string given is a NULL pointer", __func__);
return 1;
}
LOG_DEBUG(TAG, "set: %s", reply);
strncpy(udp_owner.reply, reply, sizeof(udp_owner.reply) - 1); // -1: compensate for '\0'
return 0;
}
/**
* @fn uint8_t udp_broadcast_format_reply(const owner_details_t*)
* @brief format_reply() formats all the owner's details into a string
* it formats a string using the owner's details using snprintf
* it sets this reply with @see udp_broadcast_set_owner_details_reply()
*/
static void udp_broadcast_format_reply(void) {
char mac_addr_str[UDP_BROADCAST_MAX_MAC_ADDR_LEN];
char reply_buf[UDP_BROADCAST_MAX_REPLY_SIZE];
snprintf(mac_addr_str, sizeof(mac_addr_str), "%02X:%02X:%02X:%02X:%02X:%02X", udp_owner.mac_address[0],
udp_owner.mac_address[1], udp_owner.mac_address[2], udp_owner.mac_address[3], udp_owner.mac_address[4],
udp_owner.mac_address[5]);
snprintf(reply_buf, UDP_BROADCAST_MAX_REPLY_SIZE, "%s %s %s %s", mac_addr_str, UDP_BROADCAST_REPLY_MIDDLE_TEXT, udp_owner.surname,
udp_owner.name);
udp_broadcast_set_owner_details_reply(reply_buf);
}
/**
* @fn err_t udp_broadcast_set_owner_details(owner_details_t*, const char*, const char*)
* @brief set_owner_details() is the interface that can be used in other files
* to set the owner's details
* the pointers get checked by the functions that are called in this function
*
* @param[in] name string containing the new owner's name
* @param[in] surname string containing the new owner's surname
* @return lwIP error code.
* - ERR_OK. Successful. No error occurred.
* - ERR_ARG. one or both arguments are NULL pointers
*/
err_t udp_broadcast_set_owner_details(const char* name, const char* surname) {
if (!udp_broadcast_set_owner_details_name(name) && !udp_broadcast_set_owner_details_surname(surname)) {
// If both return 0 it's okay
udp_broadcast_set_owner_details_mac();
udp_broadcast_format_reply();
return ERR_OK;
}
return ERR_ARG;
}
/**
* @fn char udp_broadcast_get_owner_details_name*(void)
* @brief get_owner_details_name() can be used to get the current owner's name
*
* @return name of owner
* this name is set by @see udp_broadcast_set_owner_details_name()
*/
char* udp_broadcast_get_owner_details_name(void) {
return udp_owner.name;
}
/**
* @fn char udp_broadcast_get_owner_details_surname*(void)
* @brief get_owner_details_surname() can be used to get the current owner's surname
*
* @return surname of owner
* this name is set by @see udp_broadcast_set_owner_details_surname()
*/
char* udp_broadcast_get_owner_details_surname(void) {
return udp_owner.surname;
}
/**
* @fn char udp_broadcast_get_owner_details_reply*(void)
* @brief get_owner_details_reply() can be used to get the current UDP reply
*
* @return reply for UDP broadcast
* this reply is formatted by @see format_reply()
*/
char* udp_broadcast_get_owner_details_reply(void) {
return udp_owner.reply;
}
/**
* @fn void udp_broadcast_check_function(const char[])
* @brief checks what the UDP datagram asked to do
* and processes the datagram if it was not @see UDP_QUESTION1
*
* @param[in] data the datagram received on port 64000
*
* @return checked
* - 0: a function was found and processed if necessary
* - 1: datagram didn't have a known function
*/
static uint8_t udp_broadcast_check_function(const char data[UDP_BROADCAST_MAX_DATA_SIZE]) {
char func[UDP_BROADCAST_MAX_FUNC_LEN];
char buffer[UDP_BROADCAST_MAX_NAME_SIZE];
uint8_t enders[UDP_BROADCAST_MAX_COLON_COMMA_COUNT];
uint8_t counter = 0;
uint8_t data_len = strlen(data);
if (strcmp(data, UDP_BROADCAST_UDP_QUESTION1) == 0) {
return 0;
}
memset(func, 0, sizeof(func));
memset(buffer, 0, sizeof(buffer));
memcpy(func,data,UDP_BROADCAST_MAX_FUNC_LEN - 1);
if (strcmp(func, "func1:") != 0) {
LOG_WARN(TAG, "%s: datagram does not contain function that's currently available", __func__);
return 1;
}
for (uint8_t i = 0; i < data_len && counter < UDP_BROADCAST_MAX_COLON_COMMA_COUNT; i++) {
if ((data[i] == ',' || data[i] == ':')) {
enders[counter] = i;
counter++;
}
}
if (enders[2] - enders[1] < UDP_BROADCAST_MAX_NAME_SIZE + 2 && data_len - enders[3] < UDP_BROADCAST_MAX_NAME_SIZE + 2
&& strncmp(data + enders[0], ":name", 5) == 0 && strncmp(data + enders[2], ", surname", 9) == 0) {
counter = 0;
for (uint8_t i = enders[1] + 2; i < enders[2]; i++) {
buffer[counter] = data[i];
counter++;
}
if (buffer[0]=='\0') {
strncpy(buffer, "name", sizeof(buffer) - 1); // -1: compensate for '\0'
}
LOG_INFO(TAG, "new owner name:%s", buffer);
udp_broadcast_set_owner_details_name(buffer);
memset(buffer, 0, sizeof(buffer));
counter = 0;
for (uint8_t i = enders[3] + 2; i < data_len; i++) {
buffer[counter] = data[i];
counter++;
}
if (buffer[0]=='\0') {
strncpy(buffer, "default", sizeof(buffer) - 1); // -1: compensate for '\0'
}
LOG_INFO(TAG, "new owner surname:%s", buffer);
udp_broadcast_set_owner_details_surname(buffer);
udp_broadcast_format_reply();
return 0;
}
LOG_WARN(TAG,"%s: function didn't receive the right formatting", __func__);
return 1;
}
/**
* @fn void udp_receive_callback(void*, struct udp_pcb*, struct pbuf*, const ip_addr_t*, u16_t)
* @brief udp_receive_callback() callback function for when a UDP packet has been received.
* it compares the data to a set string @see UDP_QUESTION1, if it's the same it sends the reply string,
* @see reply_str, back to the client
* if it was not @see UDP_QUESTION1, it checks what function was called with @see udp_broadcast_check_function()
*
* @param[in] arg a pointer to some user-defined data or context
* @param[in] connection UDP PCB to be bound with a local address ipaddr and port.
* @param[in] p packet buffer it holds the incoming UDP packet data, including its payload and length
* @param[in] addr ip_addr_t structure that contains the IP address of the sender of the UDP packet
* @param[in] port the source port number of the sender's UDP packet
*/
static void udp_receive_callback(void* arg,
struct udp_pcb* connection,
struct pbuf* p,
const ip_addr_t* addr,
u16_t port) {
struct pbuf* p_data;
size_t len;
char* pc;
char data[UDP_BROADCAST_MAX_DATA_SIZE];
char source_ip_str[16];
memset(data, 0, sizeof(data));
ipaddr_ntoa_r(addr, source_ip_str, sizeof(source_ip_str)); // Convert the source IP address to a string
if (p == NULL) {
LOG_WARN(TAG, "%s: input buffer was a NULL pointer", __func__);
return;
}
pc = (char*)p->payload;
len = p->tot_len;
if (len >= UDP_BROADCAST_MAX_DATA_SIZE) { // >= : only if it's smaller to compensate for '\0'
LOG_WARN(TAG, "%s: input buffer was bigger than or was max size %d", __func__, UDP_BROADCAST_MAX_DATA_SIZE);
return;
}
p_data = pbuf_alloc(PBUF_TRANSPORT, sizeof(udp_owner.reply), PBUF_RAM);
if (p_data == NULL) {
LOG_WARN(TAG, "%s: unable to allocate data buffer for reply", __func__);
goto defer;
}
for (size_t i = 0; i < len; i++) {
data[i] = pc[i];
}
LOG_INFO(TAG, "%s: received data from %s at port: %d: %s", __func__, source_ip_str, port, data);
LOG_INFO(TAG, "%s: checking which function was called", __func__);
if(!udp_broadcast_check_function(data)){ // Should return 0 to reply
p_data->payload = udp_owner.reply;
p_data->len = strlen(udp_owner.reply);
p_data->tot_len = strlen(udp_owner.reply);
udp_sendto(connection, p_data, addr, 64000); // QT app listens on port 64000
LOG_INFO(TAG, "%s: tried to reply to %s at port: %d: %s", __func__, source_ip_str, 64000, udp_owner.reply);
}
defer:
pbuf_free(p);
pbuf_free(p_data);
}
/**
* @fn err_t udp_broadcast_init(void)
* @brief init_UDP_server() initialises the UDP connection so that it listens for all traffic on
* port 6400
* it makes a udp_pcb, binds it to port 64000 and initializes the callback function for when data is received
*
* @return lwIP error code.
* - ERR_OK. Successful. No error occurred.
* - ERR_USE. The specified ipaddr and port are already bound to by another UDP PCB.
* - ERR_MEM. udp pcb couldn't be created
*/
err_t udp_broadcast_connection_init(void) {
struct udp_pcb* connection;
err_t err;
LOG_INFO(TAG, "%s: initializing UDP server", __func__);
connection = udp_new();
if (connection == NULL) {
LOG_WARN(TAG, "%s: Initializing UDP server failed, connection is null", __func__);
return ERR_MEM;
}
err = udp_bind(connection, IP_ANY_TYPE, 64000);
if (err != ERR_OK) {
LOG_WARN(TAG, "%s: Initializing UDP server failed, err not ok", __func__);
udp_remove(connection);
return err;
}
udp_recv(connection, udp_receive_callback, NULL);
LOG_INFO(TAG, "%s: Initializing UDP server successful, callback running", __func__);
return err;
}
/**
* @fn err_t udp_broadcast_init()
* @brief udp_broadcast_init() initializes the owner's variables and calls upon @see udp_broadcast_connection_init()
*
* @return lwIP error code.
* - ERR_OK. Successful. No error occurred.
* - ERR_USE. The specified ipaddr and port are already bound to by another UDP PCB.
* - ERR_MEM. udp pcb couldn't be created
*
* - ERR_ARG. one or both arguments of udp_broadcast_set_owner_details() are NULL pointers
*/
err_t udp_broadcast_init(uint16_t x_pos, uint16_t y_pos) {
owner_name_x_pos = x_pos;
owner_name_y_pos = y_pos;
if(udp_broadcast_set_owner_details("name", "default") != ERR_OK){
LOG_WARN(TAG, "%s: don't give NULL pointers as arguments for the owner's details", __func__);
return ERR_ARG;
}
return udp_broadcast_connection_init();
}

View File

@@ -84,7 +84,7 @@ void lcd_task(void) {
}
}
void lcd_display_text(const char* text, uint16_t x_pos, uint16_t y_pos, uint32_t color, sFONT* font) {
void lcd_display_text(const char* text, uint16_t x_pos, uint16_t y_pos, uint32_t color, uint32_t bg_color, sFONT* font) {
BSP_LCD_SelectLayer(1);
LOG_INFO(TAG, "Display text: %s @x=%d,y=%d", text, x_pos, y_pos);
@@ -94,7 +94,7 @@ void lcd_display_text(const char* text, uint16_t x_pos, uint16_t y_pos, uint32_t
}
BSP_LCD_SetTextColor(color);
BSP_LCD_SetBackColor(0);
BSP_LCD_SetBackColor(bg_color);
BSP_LCD_SetFont(font);
if (tot_length > BSP_LCD_GetXSize()) {
@@ -186,6 +186,11 @@ void lcd_clear_images(void) {
BSP_LCD_Clear(0);
}
void lcd_set_bg_color_layer0(uint32_t color){
BSP_LCD_SelectLayer(0);
BSP_LCD_Clear(color);
}
void lcd_stop_all_gifs(void) {
for (uint8_t i = 0; i < LCD_MAX_GIFS; i++) {
if (gifs[i].src != NULL) {

View File

@@ -28,7 +28,9 @@
#include "log.h"
#include "llfs.h"
#include "lcd_api.h"
#include "mqtt_application.h"
#include "tftp.h"
#include "UDP_broadcast.h"
/* USER CODE END Includes */
@@ -132,16 +134,30 @@ int main(void)
/* Initialize the tftp server */
tftp_server_init();
/* Initialize the MQTT application */
mqtt_application_init();
// Initialize the UDP broadcast service
if (udp_broadcast_init(10,255) != ERR_OK){
LOG_WARN(TAG,"error initializing udp connection, check warnings from udp_broadcast_init() or udp_broadcast_connection_init()");
}
if (udp_broadcast_set_owner_details("Joran", "Van Nieuwenhoven") != ERR_OK){
LOG_WARN(TAG,"error setting owner's details");
}
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
MX_LWIP_Process();
/* USER CODE BEGIN 3 */
MX_LWIP_Process();
lcd_task();
}
/* USER CODE END 3 */

View File

@@ -0,0 +1,414 @@
/**
* @file mqtt_application.c
* @brief mosquitto application for group assignment
* @author RobinVdB
*/
#include <string.h>
#include "httpd.h"
#include "lcd_api.h"
#include "lwip/apps/fs.h"
#include "lwip/ip_addr.h"
#include "mdns.h"
#include "mqtt.h"
#include "mqtt_application.h"
#define LOGGER_LEVEL_INFO
#include "log.h"
#define ATTEMPT_RECONNECT_AMOUNT 50
#define PUBLISH_QOS 2
#define PUBLISH_RETAIN 1
#define MQTT_SERVER_PORT 1883
#define PRINT_XPOS 50
#define PRINT_YPOS 50
#define MAX_FILES 20
#define SERVER_IP4_A 192
#define SERVER_IP4_B 168
#define SERVER_IP4_C 69
#define SERVER_IP4_D 11
#define SERVER_PORT 1883
typedef enum input_topic {
set_text,
set_text_color,
set_color,
set_image,
other_topic
} input_topic_t;
// Function prototypes
static void mqtt_pub_request_cb(void*, err_t);
static void publish_data(mqtt_client_t*, void*);
static void mqtt_incoming_publish_cb(void*, const char*, u32_t);
static void mqtt_incoming_data_cb(void*, const uint8_t*, u16_t, u8_t);
static void mqtt_sub_request_cb(void*, err_t);
static void mqtt_connection_cb(mqtt_client_t*, void*, mqtt_connection_status_t);
static void mosquitto_connect(mqtt_client_t*);
static uint32_t color_picker(char*);
static void create_publish_string(char*, char*, size_t);
// Global variables used in mqtt_incoming_publish_cb and mqtt_incoming_data_cb to give an easy to use ID to the subscribed topics
static sFONT* font;
static uint16_t xpos;
static uint16_t ypos;
static uint16_t connection_attempt_counter;
static uint32_t color;
static uint32_t bgcolor;
static input_topic_t inpub_id;
static const char* TAG = "MQTT";
/**
* @brief callback function for publishing data
*
* @param[in] arg User supplied argument to connection callback
* @param[in] result Whether the publish was successful or not
*/
static void mqtt_pub_request_cb(void* arg, err_t result) {
LOG_DEBUG(TAG, "Publish result: %d", result);
}
/**
* @brief Publishes data
* Publishes the names of all the .bmp and .gif files on the topic getImageList
*
* @param[in] client Pointer to the MQTT client
* @param[in] arg Additional argument to pass to the callback function
*/
static void publish_data(mqtt_client_t* client, void* arg) {
char pub_payload[200] = {0};
err_t err;
LOG_DEBUG(TAG, "Entering publish");
create_publish_string("*.bmp", pub_payload,sizeof(pub_payload));
err = mqtt_publish(client, "getImageList", pub_payload, strlen(pub_payload), PUBLISH_QOS, PUBLISH_RETAIN, mqtt_pub_request_cb, arg);
if (err != ERR_OK) {
LOG_DEBUG(TAG, "Publish err: %d", err);
}
pub_payload[0] = '\0';
create_publish_string("*.gif", pub_payload, sizeof(pub_payload));
err = mqtt_publish(client, "getGifList", pub_payload, strlen(pub_payload), PUBLISH_QOS, PUBLISH_RETAIN, mqtt_pub_request_cb, arg);
if (err != ERR_OK) {
LOG_DEBUG(TAG, "Publish err: %d", err);
}
}
/**
* @brief Handles incoming publish
* Callback function for when data was published to a subscribed topic
*
* @param[in] arg User supplied argument to connection callback
* @param[in] topic The topic on which an incoming publish was received
* @param[in] tot_len Length of the incoming data
*/
static void mqtt_incoming_publish_cb(void* arg, const char* topic, uint32_t tot_len) {
LOG_DEBUG(TAG, "Incoming publish at topic %s with total length %lu", topic, tot_len);
// Check for which topic a publish was received
if (strcmp(topic, "input/setText") == 0) {
inpub_id = set_text;
return;
}
if (strcmp(topic, "input/setImage") == 0) {
inpub_id = set_image;
return;
}
if (strcmp(topic, "input/setTextColor") == 0) {
inpub_id = set_text_color;
return;
}
if (strcmp(topic, "input/setColor") == 0) {
inpub_id = set_color;
return;
}
// In case of wrong topic
inpub_id = other_topic;
}
/**
* @brief Handles incoming publish data
* Handles the received data from a publish to a subscribed topic
*
* @param[in] arg User supplied argument to connection callback
* @param[in] data The incoming data
* @param[in] len Length of the data
* @param[in] flags Whether this is the last fragment of the incoming data
*/
static void mqtt_incoming_data_cb(void* arg, const uint8_t* data, uint16_t len, uint8_t flags) {
char data_buffer[len + 1];
lcd_gif_t* gif;
LOG_INFO(TAG, "Incoming publish payload with length %d, flags %d", len, flags);
if (!(flags & MQTT_DATA_FLAG_LAST)) {
LOG_WARN(TAG, "incoming data too big to fit in buffer.");
return;
}
memcpy(data_buffer, data, len);
data_buffer[len] = '\0';
switch (inpub_id) {
case set_text:
// Places text on the lcd
LOG_INFO(TAG, "incoming data on input/setText: %s.", data_buffer);
lcd_clear_text();
lcd_display_text((const char*)data_buffer, xpos, ypos, color, bgcolor, font);
break;
case set_image:
// Places an image on the lcd
LOG_INFO(TAG, "incoming data on input/setImage: %s.", data_buffer);
lcd_clear_images();
lcd_set_bg_color_layer0(bgcolor);
if (len >= 3) {
if (data_buffer[len - 3] == 'b') {
lcd_draw_img_from_fs((const char*)data_buffer, xpos, ypos);
}
if (data_buffer[len - 3] == 'g') {
gif = lcd_draw_gif_from_fs((const char*)data_buffer, xpos, ypos);
if (gif == NULL) {
LOG_INFO(TAG, "GIF could not be drawn");
}
}
}
break;
case set_text_color:
// Changes the text color for the next time text is written
LOG_INFO(TAG, "incoming data on input/setTextColor: %s.", data_buffer);
color = color_picker(data_buffer);
break;
case set_color:
// Changes the background color for the next time text is written
LOG_INFO(TAG, "incoming data on input/setColor: %s.", data_buffer);
bgcolor = color_picker(data_buffer);
break;
default:
LOG_INFO(TAG, "Publish received on wrong topic, incoming data ignored.");
}
}
/**
* @brief Callback function for outgoing subscribe request
*
* @param[in] arg User supplied argument to connection callback
* @param[in] result Result code for the subscribe request
*/
static void mqtt_sub_request_cb(void* arg, err_t result) {
LOG_DEBUG(TAG, "Subscribe result: %d", result);
}
/**
* @brief Callback function for attempting a connection
* If a connection was made setup a callback function for incoming publishes.
* subscribes to the input topics and calls the publish_data function.
*
* @param[in] client Pointer to the MQTT client
* @param[in] arg User supplied argument to connection callback
* @param[in] status Connect result code or disconnection notification
*/
static void mqtt_connection_cb(mqtt_client_t* client, void* arg, mqtt_connection_status_t status) {
err_t err;
if (status != MQTT_CONNECT_ACCEPTED) {
LOG_INFO(TAG, "mqtt_connection_cb: Disconnected, reason: %d", status);
if (connection_attempt_counter < ATTEMPT_RECONNECT_AMOUNT) {
connection_attempt_counter++;
// Try to reconnect
mosquitto_connect(client);
}
return;
}
LOG_INFO(TAG, "Successfully connected");
connection_attempt_counter = 0;
// Set up callback function for input
mqtt_set_inpub_callback(client, mqtt_incoming_publish_cb, mqtt_incoming_data_cb, arg);
// Subscribe to the topics setText, setImage, setColor and setTextcolor
err = mqtt_subscribe(client, "input/#", 1, mqtt_sub_request_cb, arg);
if (err != ERR_OK) {
LOG_DEBUG(TAG, "mqtt_subscribe return: %d", err);
}
// Publish list of images here
publish_data(client, NULL);
}
/**
* @brief Attempts to create a connection to the mosquitto broker
* Creates a mqtt client and sets up a connection callback function
*
* @param[in] client Pointer to the MQTT client
*/
static void mosquitto_connect(mqtt_client_t* client) {
struct mqtt_connect_client_info_t ci;
err_t err;
LOG_INFO(TAG, "Attempting MQTT Connection");
memset(&ci, 0, sizeof(ci));
ci.client_id = "STM32";
ip_addr_t server_ip;
IP4_ADDR(&server_ip, SERVER_IP4_A, SERVER_IP4_B, SERVER_IP4_C, SERVER_IP4_D);
uint16_t server_port = SERVER_PORT;
err = mqtt_client_connect(client, &server_ip, server_port, mqtt_connection_cb, 0, &ci);
if (err != ERR_OK) {
LOG_DEBUG(TAG, "mqtt_connect return %d", err);
return;
}
LOG_DEBUG(TAG, "Went into mqtt_client_connect; mqtt_connect return %d", err);
}
/**
* @brief Init function for the mosquitto application of the assignment
* Gives the global variables a value and calls the mosquitto_connect function
*/
uint8_t mqtt_application_init(void) {
color = LCD_BLACK;
bgcolor = LCD_WHITE;
font = LCD_FONT16;
xpos = PRINT_XPOS;
ypos = PRINT_YPOS;
connection_attempt_counter = 0;
mqtt_client_t* client = mqtt_client_new();
if (client == NULL) {
LOG_CRIT(TAG, "%s: client == NULL", __func__);
return 1;
}
LOG_DEBUG(TAG, "Starting connection test");
mosquitto_connect(client);
return 0;
}
/**
* @brief Reads the color input string and outputs it to a usable value for LCD_APi
*
* @param[in] color Input string to select a color
* @return color Define to use with the LCD_API
*/
uint32_t color_picker(char* color_in) {
for (int i = 0; i < strlen(color_in); i++) {
color_in[i] = tolower(color_in[i]);
}
if (strcmp((const char*)color_in, "blue") == 0) {
return LCD_BLUE;
}
if (strcmp((const char*)color_in, "green") == 0) {
return LCD_GREEN;
}
if (strcmp((const char*)color_in, "red") == 0) {
return LCD_RED;
}
if (strcmp((const char*)color_in, "cyan") == 0) {
return LCD_CYAN;
}
if (strcmp((const char*)color_in, "magenta") == 0) {
return LCD_MAGENTA;
}
if (strcmp((const char*)color_in, "yellow") == 0) {
return LCD_YELLOW;
}
if (strcmp((const char*)color_in, "light blue") == 0) {
return LCD_LIGHTBLUE;
}
if (strcmp((const char*)color_in, "light green") == 0) {
return LCD_LIGHTGREEN;
}
if (strcmp((const char*)color_in, "light red") == 0) {
return LCD_LIGHTRED;
}
if (strcmp((const char*)color_in, "light cyan") == 0) {
return LCD_LIGHTCYAN;
}
if (strcmp((const char*)color_in, "light magenta") == 0) {
return LCD_LIGHTMAGENTA;
}
if (strcmp((const char*)color_in, "light yellow") == 0) {
return LCD_LIGHTYELLOW;
}
if (strcmp((const char*)color_in, "dark blue") == 0) {
return LCD_DARKBLUE;
}
if (strcmp((const char*)color_in, "dark green") == 0) {
return LCD_DARKGREEN;
}
if (strcmp((const char*)color_in, "dark red") == 0) {
return LCD_DARKRED;
}
if (strcmp((const char*)color_in, "dark cyan") == 0) {
return LCD_DARKCYAN;
}
if (strcmp((const char*)color_in, "dark magenta") == 0) {
return LCD_DARKMAGENTA;
}
if (strcmp((const char*)color_in, "dark yellow") == 0) {
return LCD_DARKYELLOW;
}
if (strcmp((const char*)color_in, "white") == 0) {
return LCD_WHITE;
}
if (strcmp((const char*)color_in, "light gray") == 0) {
return LCD_LIGHTGRAY;
}
if (strcmp((const char*)color_in, "gray") == 0) {
return LCD_GRAY;
}
if (strcmp((const char*)color_in, "dark gray") == 0) {
return LCD_DARKGRAY;
}
if (strcmp((const char*)color_in, "black") == 0) {
return LCD_BLACK;
}
if (strcmp((const char*)color_in, "brown") == 0) {
return LCD_BROWN;
}
if (strcmp((const char*)color_in, "orange") == 0) {
return LCD_ORANGE;
}
if (strcmp((const char*)color_in, "transparent") == 0) {
return LCD_TRANSPARENT;
}
return LCD_BLACK;
}
/**
* @brief creates a string to publish on the getImageList topic
*
* @param[in] file_type The file extension asked to be published
* @param[in] payload_buffer The string to be published
* @param[in] buffer_size Size of payload_buffer
*/
static void create_publish_string(char* file_type, char* payload_buffer, size_t buffer_size) {
size_t num_files;
llfs_file_t file_list[MAX_FILES];
num_files = llfs_file_list(file_list, MAX_FILES, file_type);
if (num_files == 0) {
strncat(payload_buffer, "No files found of type: ", buffer_size - strlen(payload_buffer) - 1);
strncat(payload_buffer, file_type, buffer_size - strlen(payload_buffer) - 1);
LOG_INFO(TAG, "%s: No files found of type %s", __func__, file_type);
return;
}
if (strcmp(file_type, "*.bmp") == 0) {
strncat(payload_buffer, "Available images: ", buffer_size - strlen(payload_buffer) - 1);
} else if (strcmp(file_type, "*.gif") == 0) {
strncat(payload_buffer, "Available gifs: ", buffer_size - strlen(payload_buffer) - 1);
} else {
LOG_WARN(TAG, "No application for given file type: %s", file_type);
return;
}
for (size_t i = 0; i < num_files; i++) {
// Concatenate file names into the payload string
strncat(payload_buffer, file_list[i].name, buffer_size - strlen(payload_buffer) - 1);
strncat(payload_buffer, ", ", buffer_size - strlen(payload_buffer) - 1); // Add a comma between file names
}
strncat(payload_buffer, "\0", buffer_size - strlen(payload_buffer) - 1);
LOG_DEBUG(TAG, "String: %s", payload_buffer);
}

View File

@@ -167,7 +167,7 @@ void tftp_close(void* handle) {
if (handle == &virt_file[VIRT_TEXT_TXT]) {
lcd_clear_images();
lcd_clear_text();
lcd_display_text((uint8_t*)virt_file[VIRT_TEXT_TXT].data, 0, 0, LCD_COLOR_WHITE, LCD_FONT16);
lcd_display_text((uint8_t*)virt_file[VIRT_TEXT_TXT].data, 0, 0, LCD_COLOR_WHITE, LCD_TRANSPARENT, LCD_FONT16);
}
if (handle == &virt_file[VIRT_INDEX_TXT] || handle == &virt_file[VIRT_IMAGE_BMP]
@@ -299,6 +299,7 @@ void tftp_server_init(void) {
// Init the tftp server
if (tftp_init(&tftpContext_s) != ERR_OK) {
LOG_FATAL(TAG, "Could not initialize tftp server");
tftp_server_deinit();
return;
}
LOG_INFO(TAG, "tftp server initialized successfully");
@@ -321,4 +322,4 @@ void tftp_server_deinit(void) {
virt_file[VIRT_TEXT_TXT].data = NULL;
virt_file[VIRT_TEXT_TXT].len = 0;
}
}