14 Commits

Author SHA1 Message Date
L-diy
4181b98f83 Improve the Modbus TCP server
The 'modbus_tcp.c' file has been extensively rewritten to handle parsing TCP Application Data Units (ADUs), handling incoming Modbus requests, and generating appropriate responses. The modbus tcp server now interacts better with the LWIP TCP functionality, due to better utilization of the callback functions.

temp: modbus refactor
2024-02-06 12:45:42 +01:00
e5273c7012 clangformat everything 2023-12-23 21:37:08 +01:00
e19e7c9253 website backend
fix warning
2023-12-23 16:25:16 +01:00
50fe918561 tests
fix tftp tests
2023-12-23 16:17:36 +01:00
9d18c40a43 tests
Change sources and include to cmake path instead of relative path
2023-12-23 16:17:21 +01:00
Roelandts_Gert
fc87693d64 Merge pull request #35 from Sani7/website_02
Website 02
2023-12-11 14:57:16 +01:00
TeunBugwood
5279b14975 img 0 0 2023-12-11 14:45:27 +01:00
TeunBugwood
af0c6ad421 data file 2023-12-11 14:43:09 +01:00
TeunBugwood
ebd0479cd9 img 2023-12-11 14:40:50 +01:00
TeunBugwood
d1991d9abc updated tabs etc 2023-12-11 14:03:42 +01:00
TeunBugwood
5b0666920c Update main.c 2023-12-11 14:01:24 +01:00
TeunBugwood
cd2839e6ca Update website_backend.c 2023-12-11 13:34:47 +01:00
TeunBugwood
dd176256bb WBE update
Fixed some things that made the site not work. This is not the final version, just for the demo.
2023-12-11 13:31:46 +01:00
TeunBugwood
ec09cb9625 New website branch
previous was doomed, that is why I made a new brench that was updated with the main.
2023-12-11 13:20:19 +01:00
41 changed files with 252842 additions and 47087 deletions

View File

@@ -1,15 +1,11 @@
#include "about.h"
#include "ui_about.h"
About::About(QWidget *parent) :
QDialog(parent),
ui(new Ui::About)
{
About::About(QWidget* parent) : QDialog(parent), ui(new Ui::About) {
ui->setupUi(this);
ui->lblVersion->setText(ui->lblVersion->text() + " " + __DATE__ + " " + __TIME__);
}
About::~About()
{
About::~About() {
delete ui;
}

View File

@@ -7,8 +7,7 @@ namespace Ui {
class About;
}
class About : public QDialog
{
class About : public QDialog {
Q_OBJECT
public:

View File

@@ -2,8 +2,7 @@
#include <QApplication>
int main(int argc, char *argv[])
{
int main(int argc, char* argv[]) {
QApplication a(argc, argv);
MainWindow w;
w.show();

View File

@@ -1,17 +1,13 @@
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "QUdpSocket"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), ui(new Ui::MainWindow) {
ui->setupUi(this);
m_udpSocket = new QUdpSocket(this);
m_udpSocket->bind(QHostAddress::Any, UDP_PORT, QUdpSocket::ShareAddress);
connect(m_udpSocket, &QUdpSocket::readyRead,
this, &MainWindow::processPendingDatagrams);
connect(m_udpSocket, &QUdpSocket::readyRead, this, &MainWindow::processPendingDatagrams);
// Timer just to ask twice (udp)
m_timer = new QTimer(this);
@@ -32,29 +28,25 @@ MainWindow::MainWindow(QWidget *parent)
}
// //Example items for screenshot:
// QListWidgetItem * item = new QListWidgetItem(QIcon(":/res/stm32.png"), "00:80:E1:05:44:21 @ 192.168.0.11 (John Doe)");
// ui->devices->addItem(item);
// QListWidgetItem * item2 = new QListWidgetItem(QIcon(":/res/rpi.png"), "E4:5F:01:05:44:21 @ 192.168.0.11 (Jane Doe)");
// ui->devices->addItem(item2);
// QListWidgetItem * item3 = new QListWidgetItem(QIcon(":/res/unknown.png"), "44:44:44:05:44:21 @ 192.168.0.11 (Juul Doe)");
// QListWidgetItem * item = new QListWidgetItem(QIcon(":/res/stm32.png"), "00:80:E1:05:44:21 @ 192.168.0.11 (John
// Doe)"); ui->devices->addItem(item); QListWidgetItem * item2 = new QListWidgetItem(QIcon(":/res/rpi.png"),
// "E4:5F:01:05:44:21 @ 192.168.0.11 (Jane Doe)"); ui->devices->addItem(item2); QListWidgetItem * item3 = new
// QListWidgetItem(QIcon(":/res/unknown.png"), "44:44:44:05:44:21 @ 192.168.0.11 (Juul Doe)");
// ui->devices->addItem(item3);
connect(ui->actionAbout_Qt, &QAction::triggered, QApplication::aboutQt);
}
MainWindow::~MainWindow()
{
MainWindow::~MainWindow() {
delete ui;
}
void MainWindow::on_pbRefresh_clicked()
{
void MainWindow::on_pbRefresh_clicked() {
this->func = 0;
SendRequest();
m_timer->start();
}
void MainWindow::SendRequest()
{
void MainWindow::SendRequest() {
int broadcasts_send = 0;
int data_send = 0;
QUdpSocket udpSocket;
@@ -77,16 +69,15 @@ void MainWindow::SendRequest()
}
}
}
}
else if(this->func == 1){
} else if (this->func == 1) {
QHostAddress addr(ui->comboBox->currentText());
udpSocket.writeDatagram(this->data, addr, UDP_PORT);
data_send++;
}
if (broadcasts_send == 0 && data_send == 0) {
ui->pteLog->appendPlainText("Warning: no Network Interface Cards or valid IP adresses found (no broadcasts send)");
ui->pteLog->appendPlainText(
"Warning: no Network Interface Cards or valid IP adresses found (no broadcasts send)");
} else if (data_send != 0) {
ui->pteLog->appendPlainText(tr("Info: %1 message sent to %2").arg(data_send).arg(ui->comboBox->currentText()));
} else {
@@ -94,8 +85,7 @@ void MainWindow::SendRequest()
}
}
void MainWindow::processPendingDatagrams()
{
void MainWindow::processPendingDatagrams() {
QByteArray datagram;
QHostAddress host;
@@ -114,13 +104,9 @@ void MainWindow::processPendingDatagrams()
}
}
void MainWindow::processDatagram(QString msg,QString host)
{
void MainWindow::processDatagram(QString msg, QString host) {
// Append received data to log
QString logLine = tr("Van %1: \"%2\"")
.arg(host)
.arg(msg);
QString logLine = tr("Van %1: \"%2\"").arg(host).arg(msg);
ui->pteLog->appendPlainText(logLine);
// We receive our own request. So a check to see if it is the request:
@@ -133,9 +119,9 @@ void MainWindow::processDatagram(QString msg,QString host)
ui->comboBox->addItem(host);
}
// RegEx for MAC adress
QRegularExpression macRegExp("([0-9A-F]{2})[:-]([0-9A-F]{2})[:-]([0-9A-F]{2})[:-]([0-9A-F]{2})[:-]([0-9A-F]{2})[:-]([0-9A-F]{2})");
QRegularExpression macRegExp(
"([0-9A-F]{2})[:-]([0-9A-F]{2})[:-]([0-9A-F]{2})[:-]([0-9A-F]{2})[:-]([0-9A-F]{2})[:-]([0-9A-F]{2})");
QRegularExpressionMatch match = macRegExp.match(msg.toUpper());
if (match.hasMatch()) {
QString foundOUI = match.captured(1) % match.captured(2) % match.captured(3);
@@ -176,8 +162,7 @@ void MainWindow::processDatagram(QString msg,QString host)
}
}
QListWidgetItem *MainWindow::searchItem(QString MAC)
{
QListWidgetItem* MainWindow::searchItem(QString MAC) {
QListWidgetItem* item = nullptr;
for (int i = 0; i < ui->devices->count(); i++) {
item = ui->devices->item(i);
@@ -189,8 +174,7 @@ QListWidgetItem *MainWindow::searchItem(QString MAC)
return item;
}
void MainWindow::on_devices_itemDoubleClicked(QListWidgetItem *item)
{
void MainWindow::on_devices_itemDoubleClicked(QListWidgetItem* item) {
// Copy the IP to the clipboard
QString itemText = item->text();
int start = itemText.indexOf('@') + 2;
@@ -200,8 +184,7 @@ void MainWindow::on_devices_itemDoubleClicked(QListWidgetItem *item)
clipboard->setText(ip);
}
void MainWindow::on_devices_customContextMenuRequested(const QPoint &pos)
{
void MainWindow::on_devices_customContextMenuRequested(const QPoint& pos) {
// create menu
QAction copyAction(QIcon(":/res/copy-icon.png"), "Copy IP");
QMenu menu;
@@ -217,42 +200,30 @@ void MainWindow::on_devices_customContextMenuRequested(const QPoint &pos)
}
}
void MainWindow::on_actionAbout_triggered()
{
void MainWindow::on_actionAbout_triggered() {
About ab(this);
ab.exec();
}
void MainWindow::on_name_textEdited(const QString &arg1)
{
void MainWindow::on_name_textEdited(const QString& arg1) {
this->name = arg1;
}
void MainWindow::on_surname_textEdited(const QString &arg1)
{
void MainWindow::on_surname_textEdited(const QString& arg1) {
this->surname = arg1;
}
void MainWindow::on_comboBox_currentIndexChanged(int index)
{
void MainWindow::on_comboBox_currentIndexChanged(int index) {
if (index == 0) {
ui->ChangeOwner->setDisabled(true);
}
else{
} else {
ui->ChangeOwner->setDisabled(false);
}
}
void MainWindow::on_ChangeOwner_clicked()
{
void MainWindow::on_ChangeOwner_clicked() {
this->data = ("func1:name: " + this->name + ", surname: " + this->surname + "").toUtf8();
this->func = 1;
SendRequest();
m_timer->start();
}

View File

@@ -6,8 +6,8 @@
#include <QtWidgets>
#define QD qDebug() << __FILE__ << __LINE__
#include "about.h"
#include <QString>
#include "about.h"
// UDP protocol:
// REQ= Where are you?v1.0
@@ -17,11 +17,12 @@
#define UDP_ANS_OWNER "my owner is "
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
class MainWindow : public QMainWindow {
Q_OBJECT
public:

BIN
llfs-data/base.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

BIN
llfs-data/clap.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

BIN
llfs-data/dafoe.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

BIN
llfs-data/fellowkids.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

BIN
llfs-data/goose.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

122
llfs-data/index.css Normal file
View File

@@ -0,0 +1,122 @@
body{
font-family:Arial;
}
h1{
font-size: 175%;
padding: 10px;
margin: 10px;
}
h2 {
font-size: 150%;
padding: 8px;
position: absolute;
top: -6px;
left: 20px;
background-color:orangered;
color: white;
border-radius: 20px;
padding-left: 30px;
padding-right: 30px;
}
h3 {
font-size: 125%;
padding: 10px;
margin: 10px;
}
p {
padding: 10px;
margin: 10px;
}
header {
padding: 30px;
background-color: orangered;
}
header:after {
content:"";
display: block;
clear: both;
}
.logo{
width:100px;
transform:rotate(-10deg);
-moz-transform:rotate(-10deg);
-webkit-transform:rotate(-10deg);
-o-transform:rotate(-10deg);
float:left;
}
.title{
color: white;
font-size: 40px;
font: 60px "Lucida Sans Unicode", "Lucida Grande", sans-serif;
text-align: center;
padding-top: 10px;
}
.column {
flex-grow: 0;
flex-basis: 48%;
position: relative;
}
.right {
margin-left: 2%;
}
.content {
padding: 20px;
display: flex;
}
/*Geef vorm aan de vakjes op de site*/
.card {
background-color: white;
border-radius:10px;
padding:1%;
border: 1px solid black;
margin: 2% 0;
width: 100%;
padding-top: 30px;
}
.image {
width: 25%;
height: auto;
border: 10px solid transparent;
}
.image:hover {
border: 10px solid lightsalmon;
border-radius: 10px;
}
#selected {
border: 10px solid lightsalmon;
border-radius: 10px;
}
.sendButton {
position: absolute;
margin-top: 8%;
text-align: center;
margin-left: 40%;
font-size: 150%;
padding: 20px;
background-color:orangered;
color: white;
border-radius: 20px;
}
.sendButton:hover {
background-color:white;
color: orangered ;
}
/*---------------------------------------------------------------------------------------*/
footer {
background-color: orangered;
padding: 20px;
text-align: center;
border-top:20px solid lightskyblue; /*solid is vast*/
border-bottom:20px solid lightskyblue;
margin-top: 20px;
}
.foot {
color: white;
}
html{
background-color: white;
}
#myImg {
border: none;
transition: border 0.3s;
}

133
llfs-data/index.html Normal file
View File

@@ -0,0 +1,133 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<title>Webservices & Applications</title>
<link href="reset.css" rel="stylesheet" />
<link href="index.css" rel="stylesheet" />
</head>
<body>
<div>
<header>
<img class="logo" src="st.bmp" alt="STM"/>
<p class="title">
Webservices & Applications
</p>
</header>
<form action="cgi">
<div class="content row">
<div class="column left">
<div class="card">
<h2>Adjustments</h2>
<label for="send_text">Text LCD:</label>
<input type="text" id="send_text" name="vtxt"><br><br>
<label for="send_colorTxt">Select the text color:</label>
<input type="color" id="send_colorTxt" name="vktxt" value="#0000ff"><br><br>
<label for="send_colorBack">Select the background color:</label>
<input type="color" id="send_colorBack" name="vka" value="#0000ff"><br><br>
<!--<span class="sendButton" onClick="start();">
<input type="submit" value="Send">
</span>-->
<input type="submit" value="Send" class="sendButton" onClick="start();">
</div>
</div>
<div class="column right">
<div class="card">
<h2 >Images</h2>
<label for="vfo">Choose your photo:</label>
<div class="gallery" id="gallery">
<input type="hidden" id="gallery1" name="vfo" value="">
</div>
</div>
</div>
</div>
</form>
<script>
document.getElementById('send_colorBack').addEventListener('input', function() { document.body.style.backgroundColor = this.value; });
document.getElementById('send_colorTxt').addEventListener('input', function() { document.getElementById('send_text').style.color = this.value; });
/*--------------------------------------------------------------*/
// Function to load images from the folder
function loadImages() {
console.log("loadImages");
const xhttp = new XMLHttpRequest();
xhttp.onload = function() {
const imageContainer = document.getElementById('gallery');
console.log(this.responseText);
images = this.responseText.split('|');
console.log(images);
for (let i = 0; i < images.length; i++) {
console.log(images[i]);
const img = document.createElement('img');;
img.src= images[i];
img.alt = images[i];
img.className = `image ${i}`;
img.value = images[i];
img.onload = () => {
imageContainer.appendChild(img);
}
img.onclick = () => {
selecteerFoto(img, i, images[i]);
}
}
}
xhttp.open("GET", "images.info");
xhttp.send();
}
function selecteerFoto(image, i, images) {
var gallery = document.getElementById('gallery1');
var value = gallery.getAttribute('value');
var Pictures = document.querySelectorAll('.gallery img');
Pictures.forEach(function(image) {
image.removeAttribute('id');
image.removeAttribute('name');
});
image.id = "selected";
image.name = "vfo";
image.value = images[i];
gallery.setAttribute('value', images);
}
// Call the function to load the images
loadImages();
const xhr = new XMLHttpRequest();
function start() {
var send_text = document.getElementById("send_text").value;
var send_colorTxt = document.getElementById("send_colorTxt").value;
var send_colorBack = document.getElementById("send_colorBack").value;
var sendPicture = document.getElementById("selected").alt;
var url = "cgi?";
var queryString = url + "vtxt=" + send_text + "&vktxt=" + encodeURIComponent(send_colorTxt) + "&vka=" + encodeURIComponent(send_colorBack) + "&vfo=" + sendPicture;
console.log(queryString);
xhr.onreadystatechange = showAnswer;
xhr.open("GET", queryString);
xhr.send();
}
function showAnswer() {
if (xhr.readyState == 4 && xhr.status == 200) {
alert(xhr.responseText);
}
}
</script>
<footer>
<p class="foot">
Copyright &copy; Thomas More Mechelen-Antwerpen vzw -
Campus De Nayer - Professionele bachelor elektronica-ict
2023
</p>
</footer>
</div>
</body>
</html>

BIN
llfs-data/mind.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 MiB

62
llfs-data/reset.css Normal file
View File

@@ -0,0 +1,62 @@
/* http://meyerweb.com/eric/tools/css/reset/
v2.0b1 | 201101
NOTE: WORK IN PROGRESS
USE WITH CAUTION AND TEST WITH ABANDON */
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, figcaption, figure,
footer, header, hgroup, menu, nav, section, summary,
time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
outline: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article, aside, details, figcaption, figure,
footer, header, hgroup, menu, nav, section {
display: block;
}
body {
line-height: 1;
}
ol, ul {
list-style: none;
}
blockquote, q {
quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
content: '';
content: none;
}
/* remember to define visible focus styles!
:focus {
outline: ?????;
} */
/* remember to highlight inserts somehow! */
ins {
text-decoration: none;
}
del {
text-decoration: line-through;
}
table {
border-collapse: collapse;
border-spacing: 0;
}

BIN
llfs-data/webp.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

View File

@@ -60,7 +60,8 @@ int main(int argc, char** argv) {
}
// Write the header of the output file
fprintf(out_file, "/**\n"
fprintf(out_file,
"/**\n"
" * @file %s\n"
" * @brief Linked list file system (llfs) data file.\n"
" * This file was generated by mkllfs %s.\n"
@@ -125,7 +126,8 @@ int main(int argc, char** argv) {
// Make the last file the root file of the llfs
fprintf(out_file, "\n");
fprintf(out_file, "const struct llfs_data_file *llfs_root =%s%s;\n", (strcmp(prev_llfs_name, "NULL") == 0 ? " " : " &"), prev_llfs_name);
fprintf(out_file, "const struct llfs_data_file *llfs_root =%s%s;\n",
(strcmp(prev_llfs_name, "NULL") == 0 ? " " : " &"), prev_llfs_name);
// Print the number of files
printf("Successfully converted %d files.\r\n", file_count);

View File

@@ -48,21 +48,20 @@ extern "C" {
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
# include <windows.h>
#include <tchar.h>
#include <windows.h>
#pragma warning(push)
#pragma warning(disable : 4996)
#else
#include <dirent.h>
#include <libgen.h>
# include <sys/stat.h>
#include <stddef.h>
#include <sys/stat.h>
#endif
#ifdef __MINGW32__
#include <tchar.h>
#endif
/* types */
/* Windows UNICODE wide character support */
@@ -139,25 +138,21 @@ extern "C" {
/* readdir_r is a POSIX-only function, and may not be available under various
* environments/settings, e.g. MinGW. Use readdir fallback */
#if _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _BSD_SOURCE || _SVID_SOURCE ||\
_POSIX_SOURCE
#if _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _BSD_SOURCE || _SVID_SOURCE || _POSIX_SOURCE
#define _TINYDIR_HAS_READDIR_R
#endif
#if _POSIX_C_SOURCE >= 200112L
#define _TINYDIR_HAS_FPATHCONF
#include <unistd.h>
#endif
#if _BSD_SOURCE || _SVID_SOURCE || \
(_POSIX_C_SOURCE >= 200809L || _XOPEN_SOURCE >= 700)
#if _BSD_SOURCE || _SVID_SOURCE || (_POSIX_C_SOURCE >= 200809L || _XOPEN_SOURCE >= 700)
#define _TINYDIR_HAS_DIRFD
#include <sys/types.h>
#endif
#if defined _TINYDIR_HAS_FPATHCONF && defined _TINYDIR_HAS_DIRFD &&\
defined _PC_NAME_MAX
#if defined _TINYDIR_HAS_FPATHCONF && defined _TINYDIR_HAS_DIRFD && defined _PC_NAME_MAX
#define _TINYDIR_USE_FPATHCONF
#endif
#if defined __MINGW32__ || !defined _TINYDIR_HAS_READDIR_R ||\
!(defined _TINYDIR_USE_FPATHCONF || defined NAME_MAX)
#if defined __MINGW32__ || !defined _TINYDIR_HAS_READDIR_R || !(defined _TINYDIR_USE_FPATHCONF || defined NAME_MAX)
#define _TINYDIR_USE_READDIR
#endif
@@ -195,8 +190,7 @@ extern "C" {
#define _TINYDIR_FREE(_ptr) free(_ptr)
#endif /* !defined(_TINYDIR_MALLOC) */
typedef struct tinydir_file
{
typedef struct tinydir_file {
_tinydir_char_t path[_TINYDIR_PATH_MAX];
_tinydir_char_t name[_TINYDIR_FILENAME_MAX];
_tinydir_char_t* extension;
@@ -212,8 +206,7 @@ typedef struct tinydir_file
#endif
} tinydir_file;
typedef struct tinydir_dir
{
typedef struct tinydir_dir {
_tinydir_char_t path[_TINYDIR_PATH_MAX];
int has_next;
size_t n_files;
@@ -231,7 +224,6 @@ typedef struct tinydir_dir
#endif
} tinydir_dir;
/* declarations */
_TINYDIR_FUNC
@@ -263,12 +255,10 @@ size_t _tinydir_dirent_buf_size(_TINYDIR_DIR *dirp);
#endif
#endif
/* definitions*/
_TINYDIR_FUNC
int tinydir_open(tinydir_dir *dir, const _tinydir_char_t *path)
{
int tinydir_open(tinydir_dir* dir, const _tinydir_char_t* path) {
#ifndef _MSC_VER
#ifndef _TINYDIR_USE_READDIR
int error;
@@ -279,13 +269,11 @@ int tinydir_open(tinydir_dir *dir, const _tinydir_char_t *path)
#endif
_tinydir_char_t* pathp;
if (dir == NULL || path == NULL || _tinydir_strlen(path) == 0)
{
if (dir == NULL || path == NULL || _tinydir_strlen(path) == 0) {
errno = EINVAL;
return -1;
}
if (_tinydir_strlen(path) + _TINYDIR_PATH_EXTRA >= _TINYDIR_PATH_MAX)
{
if (_tinydir_strlen(path) + _TINYDIR_PATH_EXTRA >= _TINYDIR_PATH_MAX) {
errno = ENAMETOOLONG;
return -1;
}
@@ -305,8 +293,7 @@ int tinydir_open(tinydir_dir *dir, const _tinydir_char_t *path)
_tinydir_strcpy(dir->path, path);
/* Remove trailing slashes */
pathp = &dir->path[_tinydir_strlen(dir->path) - 1];
while (pathp != dir->path && (*pathp == TINYDIR_STRING('\\') || *pathp == TINYDIR_STRING('/')))
{
while (pathp != dir->path && (*pathp == TINYDIR_STRING('\\') || *pathp == TINYDIR_STRING('/'))) {
*pathp = TINYDIR_STRING('\0');
pathp++;
}
@@ -318,13 +305,11 @@ int tinydir_open(tinydir_dir *dir, const _tinydir_char_t *path)
#else
dir->_h = FindFirstFile(path_buf, &dir->_f);
#endif
if (dir->_h == INVALID_HANDLE_VALUE)
{
if (dir->_h == INVALID_HANDLE_VALUE) {
errno = ENOENT;
#else
dir->_d = _tinydir_opendir(path);
if (dir->_d == NULL)
{
if (dir->_d == NULL) {
#endif
goto bail;
}
@@ -337,15 +322,17 @@ int tinydir_open(tinydir_dir *dir, const _tinydir_char_t *path)
#else
/* allocate dirent buffer for readdir_r */
size = _tinydir_dirent_buf_size(dir->_d); /* conversion to int */
if (size == -1) return -1;
if (size == -1)
return -1;
dir->_ep = (struct _tinydir_dirent*)_TINYDIR_MALLOC(size);
if (dir->_ep == NULL) return -1;
if (dir->_ep == NULL)
return -1;
error = readdir_r(dir->_d, dir->_ep, &dir->_e);
if (error != 0) return -1;
if (error != 0)
return -1;
#endif
if (dir->_e == NULL)
{
if (dir->_e == NULL) {
dir->has_next = 0;
}
#endif
@@ -358,55 +345,45 @@ int tinydir_open(tinydir_dir *dir, const _tinydir_char_t *path)
}
_TINYDIR_FUNC
int tinydir_open_sorted(tinydir_dir *dir, const _tinydir_char_t *path)
{
int tinydir_open_sorted(tinydir_dir* dir, const _tinydir_char_t* path) {
/* Count the number of files first, to pre-allocate the files array */
size_t n_files = 0;
if (tinydir_open(dir, path) == -1)
{
if (tinydir_open(dir, path) == -1) {
return -1;
}
while (dir->has_next)
{
while (dir->has_next) {
n_files++;
if (tinydir_next(dir) == -1)
{
if (tinydir_next(dir) == -1) {
goto bail;
}
}
tinydir_close(dir);
if (n_files == 0 || tinydir_open(dir, path) == -1)
{
if (n_files == 0 || tinydir_open(dir, path) == -1) {
return -1;
}
dir->n_files = 0;
dir->_files = (tinydir_file*)_TINYDIR_MALLOC(sizeof *dir->_files * n_files);
if (dir->_files == NULL)
{
if (dir->_files == NULL) {
goto bail;
}
while (dir->has_next)
{
while (dir->has_next) {
tinydir_file* p_file;
dir->n_files++;
p_file = &dir->_files[dir->n_files - 1];
if (tinydir_readfile(dir, p_file) == -1)
{
if (tinydir_readfile(dir, p_file) == -1) {
goto bail;
}
if (tinydir_next(dir) == -1)
{
if (tinydir_next(dir) == -1) {
goto bail;
}
/* Just in case the number of files has changed between the first and
second reads, terminate without writing into unallocated memory */
if (dir->n_files == n_files)
{
if (dir->n_files == n_files) {
break;
}
}
@@ -421,10 +398,8 @@ int tinydir_open_sorted(tinydir_dir *dir, const _tinydir_char_t *path)
}
_TINYDIR_FUNC
void tinydir_close(tinydir_dir *dir)
{
if (dir == NULL)
{
void tinydir_close(tinydir_dir* dir) {
if (dir == NULL) {
return;
}
@@ -434,14 +409,12 @@ void tinydir_close(tinydir_dir *dir)
_TINYDIR_FREE(dir->_files);
dir->_files = NULL;
#ifdef _MSC_VER
if (dir->_h != INVALID_HANDLE_VALUE)
{
if (dir->_h != INVALID_HANDLE_VALUE) {
FindClose(dir->_h);
}
dir->_h = INVALID_HANDLE_VALUE;
#else
if (dir->_d)
{
if (dir->_d) {
_tinydir_closedir(dir->_d);
}
dir->_d = NULL;
@@ -454,15 +427,12 @@ void tinydir_close(tinydir_dir *dir)
}
_TINYDIR_FUNC
int tinydir_next(tinydir_dir *dir)
{
if (dir == NULL)
{
int tinydir_next(tinydir_dir* dir) {
if (dir == NULL) {
errno = EINVAL;
return -1;
}
if (!dir->has_next)
{
if (!dir->has_next) {
errno = ENOENT;
return -1;
}
@@ -473,12 +443,10 @@ int tinydir_next(tinydir_dir *dir)
#ifdef _TINYDIR_USE_READDIR
dir->_e = _tinydir_readdir(dir->_d);
#else
if (dir->_ep == NULL)
{
if (dir->_ep == NULL) {
return -1;
}
if (readdir_r(dir->_d, dir->_ep, &dir->_e) != 0)
{
if (readdir_r(dir->_d, dir->_ep, &dir->_e) != 0) {
return -1;
}
#endif
@@ -487,9 +455,7 @@ int tinydir_next(tinydir_dir *dir)
{
dir->has_next = 0;
#ifdef _MSC_VER
if (GetLastError() != ERROR_SUCCESS &&
GetLastError() != ERROR_NO_MORE_FILES)
{
if (GetLastError() != ERROR_SUCCESS && GetLastError() != ERROR_NO_MORE_FILES) {
tinydir_close(dir);
errno = EIO;
return -1;
@@ -501,11 +467,9 @@ int tinydir_next(tinydir_dir *dir)
}
_TINYDIR_FUNC
int tinydir_readfile(const tinydir_dir *dir, tinydir_file *file)
{
int tinydir_readfile(const tinydir_dir* dir, tinydir_file* file) {
const _tinydir_char_t* filename;
if (dir == NULL || file == NULL)
{
if (dir == NULL || file == NULL) {
errno = EINVAL;
return -1;
}
@@ -524,16 +488,12 @@ int tinydir_readfile(const tinydir_dir *dir, tinydir_file *file)
#else
dir->_e->d_name;
#endif
if (_tinydir_strlen(dir->path) +
_tinydir_strlen(filename) + 1 + _TINYDIR_PATH_EXTRA >=
_TINYDIR_PATH_MAX)
{
if (_tinydir_strlen(dir->path) + _tinydir_strlen(filename) + 1 + _TINYDIR_PATH_EXTRA >= _TINYDIR_PATH_MAX) {
/* the path for the file will be too long */
errno = ENAMETOOLONG;
return -1;
}
if (_tinydir_strlen(filename) >= _TINYDIR_FILENAME_MAX)
{
if (_tinydir_strlen(filename) >= _TINYDIR_FILENAME_MAX) {
errno = ENAMETOOLONG;
return -1;
}
@@ -546,17 +506,15 @@ int tinydir_readfile(const tinydir_dir *dir, tinydir_file *file)
#ifndef _MSC_VER
#ifdef __MINGW32__
if (_tstat(
#elif (defined _BSD_SOURCE) || (defined _DEFAULT_SOURCE) \
|| ((defined _XOPEN_SOURCE) && (_XOPEN_SOURCE >= 500)) \
|| ((defined _POSIX_C_SOURCE) && (_POSIX_C_SOURCE >= 200112L)) \
|| ((defined __APPLE__) && (defined __MACH__)) \
#elif (defined _BSD_SOURCE) || (defined _DEFAULT_SOURCE) || ((defined _XOPEN_SOURCE) && (_XOPEN_SOURCE >= 500)) \
|| ((defined _POSIX_C_SOURCE) && (_POSIX_C_SOURCE >= 200112L)) || ((defined __APPLE__) && (defined __MACH__)) \
|| (defined BSD)
if (lstat(
#else
if (stat(
#endif
file->path, &file->_s) == -1)
{
file->path, &file->_s)
== -1) {
return -1;
}
#endif
@@ -570,19 +528,18 @@ int tinydir_readfile(const tinydir_dir *dir, tinydir_file *file)
#endif
file->is_reg =
#ifdef _MSC_VER
!!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_NORMAL) ||
(
!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DEVICE) &&
!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) &&
!!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_NORMAL)
|| (!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DEVICE)
&& !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
&& !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) &&
#ifdef FILE_ATTRIBUTE_INTEGRITY_STREAM
!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_INTEGRITY_STREAM) &&
#endif
#ifdef FILE_ATTRIBUTE_NO_SCRUB_DATA
!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_NO_SCRUB_DATA) &&
#endif
!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_OFFLINE) &&
!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_TEMPORARY));
!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_OFFLINE)
&& !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_TEMPORARY));
#else
S_ISREG(file->_s.st_mode);
#endif
@@ -591,15 +548,12 @@ int tinydir_readfile(const tinydir_dir *dir, tinydir_file *file)
}
_TINYDIR_FUNC
int tinydir_readfile_n(const tinydir_dir *dir, tinydir_file *file, size_t i)
{
if (dir == NULL || file == NULL)
{
int tinydir_readfile_n(const tinydir_dir* dir, tinydir_file* file, size_t i) {
if (dir == NULL || file == NULL) {
errno = EINVAL;
return -1;
}
if (i >= dir->n_files)
{
if (i >= dir->n_files) {
errno = ENOENT;
return -1;
}
@@ -611,24 +565,20 @@ int tinydir_readfile_n(const tinydir_dir *dir, tinydir_file *file, size_t i)
}
_TINYDIR_FUNC
int tinydir_open_subdir_n(tinydir_dir *dir, size_t i)
{
int tinydir_open_subdir_n(tinydir_dir* dir, size_t i) {
_tinydir_char_t path[_TINYDIR_PATH_MAX];
if (dir == NULL)
{
if (dir == NULL) {
errno = EINVAL;
return -1;
}
if (i >= dir->n_files || !dir->_files[i].is_dir)
{
if (i >= dir->n_files || !dir->_files[i].is_dir) {
errno = ENOENT;
return -1;
}
_tinydir_strcpy(path, dir->_files[i].path);
tinydir_close(dir);
if (tinydir_open_sorted(dir, path) == -1)
{
if (tinydir_open_sorted(dir, path) == -1) {
return -1;
}
@@ -637,8 +587,7 @@ int tinydir_open_subdir_n(tinydir_dir *dir, size_t i)
/* Open a single file given its path */
_TINYDIR_FUNC
int tinydir_file_open(tinydir_file *file, const _tinydir_char_t *path)
{
int tinydir_file_open(tinydir_file* file, const _tinydir_char_t* path) {
tinydir_dir dir;
int result = 0;
int found = 0;
@@ -651,13 +600,11 @@ int tinydir_file_open(tinydir_file *file, const _tinydir_char_t *path)
_tinydir_char_t ext_buf[_TINYDIR_FILENAME_MAX];
#endif
if (file == NULL || path == NULL || _tinydir_strlen(path) == 0)
{
if (file == NULL || path == NULL || _tinydir_strlen(path) == 0) {
errno = EINVAL;
return -1;
}
if (_tinydir_strlen(path) + _TINYDIR_PATH_EXTRA >= _TINYDIR_PATH_MAX)
{
if (_tinydir_strlen(path) + _TINYDIR_PATH_EXTRA >= _TINYDIR_PATH_MAX) {
errno = ENAMETOOLONG;
return -1;
}
@@ -665,23 +612,13 @@ int tinydir_file_open(tinydir_file *file, const _tinydir_char_t *path)
/* Get the parent path */
#if (defined _MSC_VER || defined __MINGW32__)
#if ((defined _MSC_VER) && (_MSC_VER >= 1400))
errno = _tsplitpath_s(
path,
drive_buf, _TINYDIR_DRIVE_MAX,
dir_name_buf, _TINYDIR_FILENAME_MAX,
file_name_buf, _TINYDIR_FILENAME_MAX,
ext_buf, _TINYDIR_FILENAME_MAX);
errno = _tsplitpath_s(path, drive_buf, _TINYDIR_DRIVE_MAX, dir_name_buf, _TINYDIR_FILENAME_MAX, file_name_buf,
_TINYDIR_FILENAME_MAX, ext_buf, _TINYDIR_FILENAME_MAX);
#else
_tsplitpath(
path,
drive_buf,
dir_name_buf,
file_name_buf,
ext_buf);
_tsplitpath(path, drive_buf, dir_name_buf, file_name_buf, ext_buf);
#endif
if (errno)
{
if (errno) {
return -1;
}
@@ -695,8 +632,7 @@ int tinydir_file_open(tinydir_file *file, const _tinydir_char_t *path)
/* Emulate the behavior of dirname by returning "." for dir name if it's
empty */
if (drive_buf[0] == '\0' && dir_name_buf[0] == '\0')
{
if (drive_buf[0] == '\0' && dir_name_buf[0] == '\0') {
_tinydir_strcpy(dir_name_buf, TINYDIR_STRING("."));
}
/* Concatenate the drive letter and dir name to form full dir name */
@@ -728,29 +664,24 @@ int tinydir_file_open(tinydir_file *file, const _tinydir_char_t *path)
}
/* Open the parent directory */
if (tinydir_open(&dir, dir_name) == -1)
{
if (tinydir_open(&dir, dir_name) == -1) {
return -1;
}
/* Read through the parent directory and look for the file */
while (dir.has_next)
{
if (tinydir_readfile(&dir, file) == -1)
{
while (dir.has_next) {
if (tinydir_readfile(&dir, file) == -1) {
result = -1;
goto bail;
}
if (_tinydir_strcmp(file->name, base_name) == 0)
{
if (_tinydir_strcmp(file->name, base_name) == 0) {
/* File found */
found = 1;
break;
}
tinydir_next(&dir);
}
if (!found)
{
if (!found) {
result = -1;
errno = ENOENT;
}
@@ -761,26 +692,20 @@ int tinydir_file_open(tinydir_file *file, const _tinydir_char_t *path)
}
_TINYDIR_FUNC
void _tinydir_get_ext(tinydir_file *file)
{
void _tinydir_get_ext(tinydir_file* file) {
_tinydir_char_t* period = _tinydir_strrchr(file->name, TINYDIR_STRING('.'));
if (period == NULL)
{
if (period == NULL) {
file->extension = &(file->name[_tinydir_strlen(file->name)]);
}
else
{
} else {
file->extension = period + 1;
}
}
_TINYDIR_FUNC
int _tinydir_file_cmp(const void *a, const void *b)
{
int _tinydir_file_cmp(const void* a, const void* b) {
const tinydir_file* fa = (const tinydir_file*)a;
const tinydir_file* fb = (const tinydir_file*)b;
if (fa->is_dir != fb->is_dir)
{
if (fa->is_dir != fb->is_dir) {
return -(fa->is_dir - fb->is_dir);
}
return _tinydir_strncmp(fa->name, fb->name, _TINYDIR_FILENAME_MAX);
@@ -800,8 +725,7 @@ from https://womble.decadent.org.uk/readdir_r-advisory.html
* 255, since some systems (including at least HP-UX) incorrectly *
* define it to be a smaller value. */
_TINYDIR_FUNC
size_t _tinydir_dirent_buf_size(_TINYDIR_DIR *dirp)
{
size_t _tinydir_dirent_buf_size(_TINYDIR_DIR* dirp) {
long name_max;
size_t name_end;
/* parameter may be unused */
@@ -821,8 +745,7 @@ size_t _tinydir_dirent_buf_size(_TINYDIR_DIR *dirp)
#error "buffer size for readdir_r cannot be determined"
#endif
name_end = (size_t)offsetof(struct _tinydir_dirent, d_name) + name_max + 1;
return (name_end > sizeof(struct _tinydir_dirent) ?
name_end : sizeof(struct _tinydir_dirent));
return (name_end > sizeof(struct _tinydir_dirent) ? name_end : sizeof(struct _tinydir_dirent));
}
#endif
#endif

View File

@@ -20,13 +20,16 @@
#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_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_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"
@@ -36,7 +39,6 @@
#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

View File

@@ -13,10 +13,10 @@
#ifndef __ANIMATEDGIF__
#define __ANIMATEDGIF__
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define memcpy_P memcpy
#define PROGMEM
@@ -71,10 +71,7 @@ enum {
// RAW = 8-bit palettized pixels requiring transparent pixel handling
// COOKED = 16 or 24-bpp fully rendered pixels ready for display
//
enum {
GIF_DRAW_RAW = 0,
GIF_DRAW_COOKED
};
enum { GIF_DRAW_RAW = 0, GIF_DRAW_COOKED };
enum {
GIF_SUCCESS = 0,
@@ -89,24 +86,21 @@ enum {
GIF_ERROR_MEMORY
};
typedef struct gif_file_tag
{
typedef struct gif_file_tag {
int32_t iPos; // current file position
int32_t iSize; // file size
uint8_t* pData; // memory file pointer
void* fHandle; // class pointer to File/SdFat or whatever you want
} GIFFILE;
typedef struct gif_info_tag
{
typedef struct gif_info_tag {
int32_t iFrameCount; // total frames in file
int32_t iDuration; // duration of animation in milliseconds
int32_t iMaxDelay; // maximum frame delay
int32_t iMinDelay; // minimum frame delay
} GIFINFO;
typedef struct gif_draw_tag
{
typedef struct gif_draw_tag {
int iX, iY; // Corner offset of this frame on the canvas
int y; // current line being drawn (0 = top line of image)
int iWidth, iHeight; // size of this frame
@@ -132,8 +126,7 @@ typedef void (GIF_FREE_CALLBACK)(void *buffer);
//
// our private structure to hold a GIF image decode state
//
typedef struct gif_image_tag
{
typedef struct gif_image_tag {
int iWidth, iHeight, iCanvasWidth, iCanvasHeight;
int iX, iY; // GIF corner offset
int iBpp;
@@ -171,16 +164,22 @@ typedef struct gif_image_tag
//
// The GIF class wraps portable C code which does the actual work
//
class AnimatedGIF
{
class AnimatedGIF {
public:
int open(uint8_t* pData, int iDataSize, GIF_DRAW_CALLBACK* pfnDraw);
int openFLASH(uint8_t* pData, int iDataSize, GIF_DRAW_CALLBACK* pfnDraw);
int open(const char *szFilename, GIF_OPEN_CALLBACK *pfnOpen, GIF_CLOSE_CALLBACK *pfnClose, GIF_READ_CALLBACK *pfnRead, GIF_SEEK_CALLBACK *pfnSeek, GIF_DRAW_CALLBACK *pfnDraw);
int open(const char* szFilename,
GIF_OPEN_CALLBACK* pfnOpen,
GIF_CLOSE_CALLBACK* pfnClose,
GIF_READ_CALLBACK* pfnRead,
GIF_SEEK_CALLBACK* pfnSeek,
GIF_DRAW_CALLBACK* pfnDraw);
void close();
void reset();
void begin(unsigned char ucPaletteType = GIF_PALETTE_RGB565_LE);
void begin(int iEndian, unsigned char ucPaletteType) { begin(ucPaletteType); };
void begin(int iEndian, unsigned char ucPaletteType) {
begin(ucPaletteType);
};
int playFrame(bool bSync, int* delayMilliseconds, void* pUser = NULL);
int getCanvasWidth();
int allocFrameBuf(GIF_ALLOC_CALLBACK* pfnAlloc);

View File

@@ -7,15 +7,15 @@
#ifndef INC_LCD_API_H_
#define INC_LCD_API_H_
#include <ctype.h>
#include <stdbool.h>
#include <string.h>
#include <ctype.h>
#define LOGGER_LEVEL_ALL
#include "log.h"
#include "../../Drivers/BSP/STM32746G-Discovery/stm32746g_discovery_lcd.h"
#include "llfs.h"
#include "gifdec.h"
#include "llfs.h"
#include "log.h"
/**
* @brief The maximum amount of GIFs that can be displayed at the same time
@@ -61,7 +61,6 @@
#define LCD_FONT20 (&Font20)
#define LCD_FONT24 (&Font24)
extern LTDC_HandleTypeDef hLtdcHandler;
typedef struct {
@@ -121,7 +120,12 @@ void lcd_display_text(const char* text, uint32_t x_pos, uint32_t y_pos, uint32_t
* @param[in] y_size Height of image
* @param[in] color_mode Color mode (see defined color modes above in file)
*/
void lcd_draw_raw_img(const void* p_src, uint32_t x_pos, uint32_t y_pos, uint32_t x_size, uint32_t y_size, uint32_t color_mode);
void lcd_draw_raw_img(const void* p_src,
uint32_t x_pos,
uint32_t y_pos,
uint32_t x_size,
uint32_t y_size,
uint32_t color_mode);
/**
* @brief Draw BMP image on screen
@@ -187,7 +191,8 @@ void lcd_stop_all_gifs(void);
/**
* @brief Draw GIF image on screen from memory
* Draw GIF image from memory to the LCD screen at position X, Y
* @warning If the GIF has a loop count specified, it will stop after the specified amount of loops and the lcd_gif_t handle will be invalid for further use
* @warning If the GIF has a loop count specified, it will stop after the specified amount of loops and the lcd_gif_t
* handle will be invalid for further use
* @note Before drawing over a GIF, make sure to call lcd_stop_gif(), otherwise the GIF will keep overwriting the screen
*
* @param src Pointer to the GIF image data
@@ -201,7 +206,8 @@ lcd_gif_t* lcd_draw_gif(uint8_t* src, size_t size, uint32_t x_pos, uint32_t y_po
/**
* @brief Draw GIF image on screen from filesystem
* Draw GIF image from filesystem to the LCD screen at position X, Y
* @warning If the GIF has a loop count specified, it will stop after the specified amount of loops and the lcd_git_t handle will be invalidated
* @warning If the GIF has a loop count specified, it will stop after the specified amount of loops and the lcd_git_t
* handle will be invalidated
* @note Before drawing over a GIF, make sure to call lcd_stop_gif(), otherwise the GIF will keep overwriting the screen
*
* @param name The filename of the GIF image
@@ -214,7 +220,8 @@ lcd_gif_t* lcd_draw_gif_from_fs(const char* name, uint32_t x_pos, uint32_t y_pos
/**
* @brief Draw GIF image on screen from filesystem
* Draw GIF image from filesystem to the LCD screen at position X, Y
* @warning If the GIF has a loop count specified, it will stop after the specified amount of loops and the lcd_git_t handle will be invalidated
* @warning If the GIF has a loop count specified, it will stop after the specified amount of loops and the lcd_git_t
* handle will be invalidated
* @note Before drawing over a GIF, make sure to call lcd_stop_gif(), otherwise the GIF will keep overwriting the screen
*
* @param file The pointer to the llfs_file_t

View File

@@ -11,8 +11,8 @@
#ifndef LOG_H
#define LOG_H
#include <stdint.h>
#include <errno.h>
#include <stdint.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/times.h>
@@ -88,27 +88,37 @@
#endif
#if LOGGER_LEVEL <= 1
#define LOG_DEBUG(tag, fmt, ...) printf(LOG_COLOR_D"[Debug] (%8lu) [%s]: " fmt LOG_RESET_COLOR "\r\n", logger_get_timestamp(), tag, ##__VA_ARGS__)
#define LOG_DEBUG(tag, fmt, ...) \
printf(LOG_COLOR_D "[Debug] (%8lu) [%s]: " fmt LOG_RESET_COLOR "\r\n", logger_get_timestamp(), tag, \
##__VA_ARGS__)
#else
#define LOG_DEBUG(tag, fmt, ...)
#endif
#if LOGGER_LEVEL <= 2
#define LOG_INFO(tag, fmt, ...) printf(LOG_COLOR_I"[Info] (%8lu) [%s]: " fmt LOG_RESET_COLOR "\r\n", logger_get_timestamp(), tag, ##__VA_ARGS__)
#define LOG_INFO(tag, fmt, ...) \
printf(LOG_COLOR_I "[Info] (%8lu) [%s]: " fmt LOG_RESET_COLOR "\r\n", logger_get_timestamp(), tag, \
##__VA_ARGS__)
#else
#define LOG_INFO(tag, fmt, ...)
#endif
#if LOGGER_LEVEL <= 3
#define LOG_WARN(tag, fmt, ...) printf(LOG_COLOR_W"[Warning] (%8lu) [%s]: " fmt LOG_RESET_COLOR "\r\n", logger_get_timestamp(), tag, ##__VA_ARGS__)
#define LOG_WARN(tag, fmt, ...) \
printf(LOG_COLOR_W "[Warning] (%8lu) [%s]: " fmt LOG_RESET_COLOR "\r\n", logger_get_timestamp(), tag, \
##__VA_ARGS__)
#else
#define LOG_WARN(tag, fmt, ...)
#endif
#if LOGGER_LEVEL <= 4
#define LOG_CRIT(tag, fmt, ...) printf(LOG_COLOR_C"[Critical] (%8lu) [%s]: " fmt LOG_RESET_COLOR "\r\n", logger_get_timestamp(), tag, ##__VA_ARGS__)
#define LOG_CRIT(tag, fmt, ...) \
printf(LOG_COLOR_C "[Critical] (%8lu) [%s]: " fmt LOG_RESET_COLOR "\r\n", logger_get_timestamp(), tag, \
##__VA_ARGS__)
#else
#define LOG_CRIT(tag, fmt, ...)
#endif
#if LOGGER_LEVEL <= 4
#define LOG_FATAL(tag, fmt, ...) printf(LOG_COLOR_F"[Fatal] (%8lu) [%s]: " fmt LOG_RESET_COLOR "\r\n", logger_get_timestamp(), tag, ##__VA_ARGS__)
#define LOG_FATAL(tag, fmt, ...) \
printf(LOG_COLOR_F "[Fatal] (%8lu) [%s]: " fmt LOG_RESET_COLOR "\r\n", logger_get_timestamp(), tag, \
##__VA_ARGS__)
#else
#define LOG_FATAL(tag, fmt, ...)
#endif

View File

@@ -1,29 +1,20 @@
/**
* @file modbus_tcp.h
*
* @brief TCP Modbus handler
* @date Nov 6, 2023
* @brief TCP Modbus server
* @date Nov 29, 2023
* @author Obe
* @author Lorenz C.
*/
#ifndef INC_MODBUS_H_
#define INC_MODBUS_H_
#define MODBUSPORT 502 // 502 is the default
#include <tcp.h>
#include <string.h>
#include <stdint.h>
#include <stdio.h>
#include "lcd_api.h"
#include "llfs.h"
#define MODBUS_TCP_PORT 502
/**
* @fn void modbus_init
* @brief Initializes the modbus tcp
* @brief Initializes the modbus tcp server
*/
void modbus_init(void);
void modbus_tcp_init(void);
#endif /* INC_MODBUS_H_ */

View File

@@ -7,15 +7,13 @@
#ifndef INC_TCP_CMD_H_
#define INC_TCP_CMD_H_
#include <stdio.h>
#include <string.h>
#include <tcp.h>
#include "lcd_api.h"
#include "llfs.h"
#include "log.h"
#include <tcp.h>
#include <stdio.h>
#include <string.h>
void tcp_cmd_init(void);
#endif /* INC_TCP_CMD_H_ */

View File

@@ -0,0 +1,82 @@
/**
* @file website_backend.h
* @brief Backend for the website to operate
* @author Toon B.
*/
#ifndef INC_WEBSITE_BACKEND_
#define INC_WEBSITE_BACKEND_
/* Include headers C lib */
#include <stdint.h>
#include <string.h>
/* Defines */
#define LOGGER_LEVEL_ALL
#define STR_MAX 200
#define CLR_MAX 11
#define IMG_X 0
#define IMG_Y 0
#define SD_IMG "st.bmp"
/* Include headers */
#include "httpd.h"
#include "lcd_api.h"
#include "llfs.h"
#include "log.h"
#include "lwip.h"
#include "lwip/apps/fs.h"
/**
* @fn void wbe_init(void)
* @brief Initialise the http server. It also sets the beginning screen
* of the display.
*/
void wbe_init(void);
/**
* @fn int fs_open_custom(struct fs_file*, const char*)
* @brief Function is called when the frontend wants to reach/open a file
* or endpoint on the backend.
*
* @param[in] file, the file pointer of the file that the frontend wants
* @param[in] name, the name of the file or endpoint that the frontend asks for
* @return[out] returns 0 if file or endpoint was not found, 1 if it was found
*/
int fs_open_custom(struct fs_file* file, const char* name);
/**
* @fn void fs_close_custom(struct fs_file*)
* @brief Called when closing files and endpoints
*
* @param[in] file, the file pointer of the file that the frontend wants
*/
void fs_close_custom(struct fs_file* file);
/**
* @fn void httpd_cgi_handler(struct fs_file*, const char*, int, char**, char**)
* @brief When data is send from the frontend to the backend with the CGI
* tag and parameters, this function is called. With this comes parameters that
* we want to read for further use.
*
* @param[in] file, the file pointer of the file that the frontend wants
* @param[in] uri, the URI that is given to the backend from the frontend (here cgi)
* @param[in] num_parm, the number of parameters that is given with the URI
* @param[in] pc_param, contains the name of each parameter
* @param[in] pc_value, is the value of each parameter
*/
void httpd_cgi_handler(struct fs_file* file, const char* uri, int num_parm, char** pc_param, char** pc_value);
/**
* @fn void wbe_display(const char*, const uint32_t, const uint32_t, const char*)
* @brief Function displays the sentence, its color, the background and the given
* image on the LCD screen.
*
* @param[in] txt, is the text string that needs to be displayed
* @param[in] txt_color, the color of the wanted string
* @param[in] background_color, color of the background of the screen
* @param[in] image, name of the wanted image to be displayed
*/
void wbe_display(const char* txt, const uint32_t txt_color, const uint32_t background_color, const char* image);
#endif /* INC_WEBSITE_BACKEND_ */

View File

@@ -56,13 +56,11 @@ static void udp_broadcast_name_to_lcd(void){
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);
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
@@ -76,7 +74,6 @@ static void udp_broadcast_name_to_lcd(void){
*/
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;
@@ -144,8 +141,8 @@ static void udp_broadcast_format_reply(void) {
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);
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);
}
@@ -163,7 +160,6 @@ static void udp_broadcast_format_reply(void) {
*/
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();
@@ -245,9 +241,9 @@ static uint8_t udp_broadcast_check_function(const char data[UDP_BROADCAST_MAX_DA
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) {
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];
@@ -336,7 +332,6 @@ static void udp_receive_callback(void* arg,
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);
@@ -375,8 +370,6 @@ err_t udp_broadcast_connection_init(void) {
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()

View File

@@ -41,8 +41,7 @@ static int32_t seekFile(GIFFILE *pFile, int32_t iPosition);
static void closeFile(void* handle);
// C API
int GIF_openRAM(GIFIMAGE *pGIF, uint8_t *pData, int iDataSize, GIF_DRAW_CALLBACK *pfnDraw)
{
int GIF_openRAM(GIFIMAGE* pGIF, uint8_t* pData, int iDataSize, GIF_DRAW_CALLBACK* pfnDraw) {
pGIF->iError = GIF_SUCCESS;
pGIF->pfnRead = readMem;
pGIF->pfnSeek = seekMem;
@@ -54,20 +53,17 @@ int GIF_openRAM(GIFIMAGE *pGIF, uint8_t *pData, int iDataSize, GIF_DRAW_CALLBACK
return GIFInit(pGIF);
} /* GIF_openRAM() */
void GIF_close(GIFIMAGE *pGIF)
{
void GIF_close(GIFIMAGE* pGIF) {
if (pGIF->pfnClose)
(*pGIF->pfnClose)(pGIF->GIFFile.fHandle);
} /* GIF_close() */
void GIF_begin(GIFIMAGE *pGIF, unsigned char ucPaletteType)
{
void GIF_begin(GIFIMAGE* pGIF, unsigned char ucPaletteType) {
memset(pGIF, 0, sizeof(GIFIMAGE));
pGIF->ucPaletteType = ucPaletteType;
} /* GIF_begin() */
void GIF_reset(GIFIMAGE *pGIF)
{
void GIF_reset(GIFIMAGE* pGIF) {
(*pGIF->pfnSeek)(&pGIF->GIFFile, 0);
} /* GIF_reset() */
@@ -77,8 +73,7 @@ void GIF_reset(GIFIMAGE *pGIF)
// 0 = good decode, no more frames
// -1 = error
//
int GIF_playFrame(GIFIMAGE *pGIF, int *delayMilliseconds, void *pUser)
{
int GIF_playFrame(GIFIMAGE* pGIF, int* delayMilliseconds, void* pUser) {
int rc;
if (delayMilliseconds)
@@ -87,17 +82,14 @@ int GIF_playFrame(GIFIMAGE *pGIF, int *delayMilliseconds, void *pUser)
{
(*pGIF->pfnSeek)(&pGIF->GIFFile, 0); // seek to start
}
if (GIFParseInfo(pGIF, 0))
{
if (GIFParseInfo(pGIF, 0)) {
pGIF->pUser = pUser;
if (pGIF->iError == GIF_EMPTY_FRAME) // don't try to decode it
return 0;
rc = DecodeLZW(pGIF, 0);
if (rc != 0) // problem
return 0;
}
else
{
} else {
return 0; // error parsing the frame info, we may be at the end of the file
}
// Return 1 for more frames or 0 if this was the last frame
@@ -106,23 +98,19 @@ int GIF_playFrame(GIFIMAGE *pGIF, int *delayMilliseconds, void *pUser)
return (pGIF->GIFFile.iPos < pGIF->GIFFile.iSize - 1);
} /* GIF_playFrame() */
int GIF_getCanvasWidth(GIFIMAGE *pGIF)
{
int GIF_getCanvasWidth(GIFIMAGE* pGIF) {
return pGIF->iCanvasWidth;
} /* GIF_getCanvasWidth() */
int GIF_getCanvasHeight(GIFIMAGE *pGIF)
{
int GIF_getCanvasHeight(GIFIMAGE* pGIF) {
return pGIF->iCanvasHeight;
} /* GIF_getCanvasHeight() */
int GIF_getLoopCount(GIFIMAGE *pGIF)
{
int GIF_getLoopCount(GIFIMAGE* pGIF) {
return pGIF->iRepeatCount;
} /* GIF_getLoopCount() */
int GIF_getComment(GIFIMAGE *pGIF, char *pDest)
{
int GIF_getComment(GIFIMAGE* pGIF, char* pDest) {
int32_t iOldPos;
iOldPos = pGIF->GIFFile.iPos; // keep old position
@@ -134,8 +122,7 @@ int GIF_getComment(GIFIMAGE *pGIF, char *pDest)
} /* GIF_getComment() */
int GIF_getLastError(GIFIMAGE *pGIF)
{
int GIF_getLastError(GIFIMAGE* pGIF) {
return pGIF->iError;
} /* GIF_getLastError() */
@@ -143,8 +130,7 @@ int GIF_getLastError(GIFIMAGE *pGIF)
//
// Helper functions for memory based images
//
static int32_t readMem(GIFFILE *pFile, uint8_t *pBuf, int32_t iLen)
{
static int32_t readMem(GIFFILE* pFile, uint8_t* pBuf, int32_t iLen) {
int32_t iBytesRead;
iBytesRead = iLen;
@@ -157,8 +143,7 @@ static int32_t readMem(GIFFILE *pFile, uint8_t *pBuf, int32_t iLen)
return iBytesRead;
} /* readMem() */
static int32_t readFLASH(GIFFILE *pFile, uint8_t *pBuf, int32_t iLen)
{
static int32_t readFLASH(GIFFILE* pFile, uint8_t* pBuf, int32_t iLen) {
int32_t iBytesRead;
iBytesRead = iLen;
@@ -171,31 +156,31 @@ static int32_t readFLASH(GIFFILE *pFile, uint8_t *pBuf, int32_t iLen)
return iBytesRead;
} /* readFLASH() */
static int32_t seekMem(GIFFILE *pFile, int32_t iPosition)
{
if (iPosition < 0) iPosition = 0;
else if (iPosition >= pFile->iSize) iPosition = pFile->iSize-1;
static int32_t seekMem(GIFFILE* pFile, int32_t iPosition) {
if (iPosition < 0)
iPosition = 0;
else if (iPosition >= pFile->iSize)
iPosition = pFile->iSize - 1;
pFile->iPos = iPosition;
return iPosition;
} /* seekMem() */
#if defined(__LINUX__) || defined(__MCUXPRESSO)
static void closeFile(void *handle)
{
static void closeFile(void* handle) {
fclose((FILE*)handle);
} /* closeFile() */
static int32_t seekFile(GIFFILE *pFile, int32_t iPosition)
{
if (iPosition < 0) iPosition = 0;
else if (iPosition >= pFile->iSize) iPosition = pFile->iSize-1;
static int32_t seekFile(GIFFILE* pFile, int32_t iPosition) {
if (iPosition < 0)
iPosition = 0;
else if (iPosition >= pFile->iSize)
iPosition = pFile->iSize - 1;
pFile->iPos = iPosition;
fseek((FILE*)pFile->fHandle, iPosition, SEEK_SET);
return iPosition;
} /* seekMem() */
static int32_t readFile(GIFFILE *pFile, uint8_t *pBuf, int32_t iLen)
{
static int32_t readFile(GIFFILE* pFile, uint8_t* pBuf, int32_t iLen) {
int32_t iBytesRead;
iBytesRead = iLen;
@@ -218,8 +203,7 @@ static int32_t readFile(GIFFILE *pFile, uint8_t *pBuf, int32_t iLen)
// returns 1 for success, 0 for failure
// Fills in the canvas size of the GIFIMAGE structure
//
static int GIFInit(GIFIMAGE *pGIF)
{
static int GIFInit(GIFIMAGE* pGIF) {
pGIF->GIFFile.iPos = 0; // start at beginning of file
if (!GIFParseInfo(pGIF, 1)) // gather info for the first frame
return 0; // something went wrong; not a GIF file?
@@ -237,8 +221,7 @@ static int GIFInit(GIFIMAGE *pGIF)
// and return the canvas size only
// Returns 1 for success, 0 for failure
//
static int GIFParseInfo(GIFIMAGE *pPage, int bInfoOnly)
{
static int GIFParseInfo(GIFIMAGE* pPage, int bInfoOnly) {
int i, j, iColorTableBits;
int iBytesRead;
unsigned char c, *p;
@@ -282,10 +265,8 @@ static int GIFParseInfo(GIFIMAGE *pPage, int bInfoOnly)
{ // by default, convert to byte-reversed RGB565 for immediate use
// Read enough additional data for the color table
iBytesRead += (*pPage->pfnRead)(&pPage->GIFFile, &pPage->ucFileBuf[iBytesRead], 3 * (1 << iColorTableBits));
if (pPage->ucPaletteType == GIF_PALETTE_RGB565_LE || pPage->ucPaletteType == GIF_PALETTE_RGB565_BE)
{
for (i=0; i<(1<<iColorTableBits); i++)
{
if (pPage->ucPaletteType == GIF_PALETTE_RGB565_LE || pPage->ucPaletteType == GIF_PALETTE_RGB565_BE) {
for (i = 0; i < (1 << iColorTableBits); i++) {
uint16_t usRGB565;
usRGB565 = ((p[iOffset] >> 3) << 11); // R
usRGB565 |= ((p[iOffset + 1] >> 2) << 5); // G
@@ -296,8 +277,7 @@ static int GIFParseInfo(GIFIMAGE *pPage, int bInfoOnly)
pPage->pPalette[i] = __builtin_bswap16(usRGB565); // SPI wants MSB first
iOffset += 3;
}
}
else // just copy it as-is
} else // just copy it as-is
{
memcpy(pPage->pPalette, &p[iOffset], (1 << iColorTableBits) * 3);
iOffset += (1 << iColorTableBits) * 3;
@@ -316,7 +296,8 @@ static int GIFParseInfo(GIFIMAGE *pPage, int bInfoOnly)
{
pPage->ucGIFBits = p[iOffset + 1]; // packed fields
pPage->iFrameDelay = (INTELSHORT(&p[iOffset + 2])) * 10; // delay in ms
if (pPage->iFrameDelay <= 1) // 0-1 is going to make it run at 60fps; use 100 (10fps) as a reasonable substitute
if (pPage->iFrameDelay
<= 1) // 0-1 is going to make it run at 60fps; use 100 (10fps) as a reasonable substitute
pPage->iFrameDelay = 100;
if (pPage->ucGIFBits & 1) // transparent color is used
pPage->ucTransparent = p[iOffset + 4]; // transparent color index
@@ -331,7 +312,8 @@ static int GIFParseInfo(GIFIMAGE *pPage, int bInfoOnly)
c = p[iOffset++]; /* Block length */
if ((iBytesRead - iOffset) < (c + 32)) // need to read more data first
{
memmove(pPage->ucFileBuf, &pPage->ucFileBuf[iOffset], (iBytesRead-iOffset)); // move existing data down
memmove(pPage->ucFileBuf, &pPage->ucFileBuf[iOffset],
(iBytesRead - iOffset)); // move existing data down
iBytesRead -= iOffset;
iStartPos += iOffset;
iOffset = 0;
@@ -339,8 +321,7 @@ static int GIFParseInfo(GIFIMAGE *pPage, int bInfoOnly)
}
if (c == 11) // fixed block length
{ // Netscape app block contains the repeat count
if (memcmp(&p[iOffset], "NETSCAPE2.0", 11) == 0)
{
if (memcmp(&p[iOffset], "NETSCAPE2.0", 11) == 0) {
if (p[iOffset + 11] == 3 && p[iOffset + 12] == 1) // loop count
pPage->iRepeatCount = INTELSHORT(&p[iOffset + 13]);
}
@@ -373,7 +354,8 @@ static int GIFParseInfo(GIFIMAGE *pPage, int bInfoOnly)
c = p[iOffset++]; /* Block length */
if ((iBytesRead - iOffset) < (c + 32)) // need to read more data first
{
memmove(pPage->ucFileBuf, &pPage->ucFileBuf[iOffset], (iBytesRead-iOffset)); // move existing data down
memmove(pPage->ucFileBuf, &pPage->ucFileBuf[iOffset],
(iBytesRead - iOffset)); // move existing data down
iBytesRead -= iOffset;
iStartPos += iOffset;
iOffset = 0;
@@ -392,8 +374,7 @@ static int GIFParseInfo(GIFIMAGE *pPage, int bInfoOnly)
pPage->iError = GIF_DECODE_ERROR;
return 0;
} /* switch */
}
else // invalid byte, stop decoding
} else // invalid byte, stop decoding
{
if (pPage->GIFFile.iSize - iStartPos < 32) // non-image bytes at end of file?
pPage->iError = GIF_EMPTY_FRAME;
@@ -430,10 +411,8 @@ static int GIFParseInfo(GIFIMAGE *pPage, int bInfoOnly)
j = (1 << ((pPage->ucMap & 7) + 1));
// Read enough additional data for the color table
iBytesRead += (*pPage->pfnRead)(&pPage->GIFFile, &pPage->ucFileBuf[iBytesRead], j * 3);
if (pPage->ucPaletteType == GIF_PALETTE_RGB565_LE || pPage->ucPaletteType == GIF_PALETTE_RGB565_BE)
{
for (i=0; i<j; i++)
{
if (pPage->ucPaletteType == GIF_PALETTE_RGB565_LE || pPage->ucPaletteType == GIF_PALETTE_RGB565_BE) {
for (i = 0; i < j; i++) {
uint16_t usRGB565;
usRGB565 = ((p[iOffset] >> 3) << 11); // R
usRGB565 |= ((p[iOffset + 1] >> 2) << 5); // G
@@ -444,8 +423,7 @@ static int GIFParseInfo(GIFIMAGE *pPage, int bInfoOnly)
pPage->pLocalPalette[i] = __builtin_bswap16(usRGB565); // SPI wants MSB first
iOffset += 3;
}
}
else // just copy it as-is
} else // just copy it as-is
{
memcpy(pPage->pLocalPalette, &p[iOffset], j * 3);
iOffset += j * 3;
@@ -459,18 +437,15 @@ static int GIFParseInfo(GIFIMAGE *pPage, int bInfoOnly)
// into "pure" LZW
pPage->iLZWSize = 0; // we're starting with no LZW data yet
c = 1; // get chunk length
while (c && iOffset < iBytesRead)
{
while (c && iOffset < iBytesRead) {
// Serial.printf("iOffset=%d, iBytesRead=%d\n", iOffset, iBytesRead);
c = p[iOffset++]; // get chunk length
// Serial.printf("Chunk size = %d\n", c);
if (c <= (iBytesRead - iOffset))
{
if (c <= (iBytesRead - iOffset)) {
memcpy(&pPage->ucLZW[pPage->iLZWSize], &p[iOffset], c);
pPage->iLZWSize += c;
iOffset += c;
}
else // partial chunk in our buffer
} else // partial chunk in our buffer
{
int iPartialLen = (iBytesRead - iOffset);
memcpy(&pPage->ucLZW[pPage->iLZWSize], &p[iOffset], iPartialLen);
@@ -484,8 +459,7 @@ static int GIFParseInfo(GIFIMAGE *pPage, int bInfoOnly)
}
// seeking on an SD card is VERY VERY SLOW, so use the data we've already read by de-chunking it
// in this case, there's too much data, so we have to seek backwards a bit
if (iOffset < iBytesRead)
{
if (iOffset < iBytesRead) {
// Serial.printf("Need to seek back %d bytes\n", iBytesRead - iOffset);
(*pPage->pfnSeek)(&pPage->GIFFile, iStartPos + iOffset); // position file to new spot
}
@@ -494,8 +468,7 @@ static int GIFParseInfo(GIFIMAGE *pPage, int bInfoOnly)
//
// Gather info about an animated GIF file
//
int GIF_getInfo(GIFIMAGE *pPage, GIFINFO *pInfo)
{
int GIF_getInfo(GIFIMAGE* pPage, GIFINFO* pInfo) {
int iOff, iNumFrames;
int iDelay, iMaxDelay, iMinDelay, iTotalDelay;
int iReadAmount;
@@ -526,8 +499,7 @@ int GIF_getInfo(GIFIMAGE *pPage, GIFINFO *pInfo)
while (!bDone) // && iNumFrames < MAX_FRAMES)
{
bExt = 1; /* skip extension blocks */
while (bExt && iOff < iDataAvailable)
{
while (bExt && iOff < iDataAvailable) {
if ((iDataAvailable - iOff) < 258) // need to read more data first
{
memmove(cBuf, &cBuf[iOff], (iDataAvailable - iOff)); // move existing data down
@@ -538,8 +510,7 @@ int GIF_getInfo(GIFIMAGE *pPage, GIFINFO *pInfo)
iDataRemaining -= iReadAmount;
// lFileOff += iReadAmount;
}
switch(cBuf[iOff])
{
switch (cBuf[iOff]) {
case 0x3b: /* End of file */
/* we were fooled into thinking there were more pages */
iNumFrames--;
@@ -558,8 +529,10 @@ int GIF_getInfo(GIFIMAGE *pPage, GIFINFO *pInfo)
iDelay = 2;
iDelay *= 10; // turn JIFFIES into milliseconds
iTotalDelay += iDelay;
if (iDelay > iMaxDelay) iMaxDelay = iDelay;
else if (iDelay < iMinDelay) iMinDelay = iDelay;
if (iDelay > iMaxDelay)
iMaxDelay = iDelay;
else if (iDelay < iMinDelay)
iMinDelay = iDelay;
// (cBuf[iOff+6]; // transparent color index
}
iOff += 2; /* skip to length */
@@ -567,8 +540,7 @@ int GIF_getInfo(GIFIMAGE *pPage, GIFINFO *pInfo)
iOff++;
// block terminator or optional sub blocks
c = cBuf[iOff++]; /* Skip any sub-blocks */
while (c)
{
while (c) {
iOff += (int)c;
c = cBuf[iOff++];
if ((iDataAvailable - iOff) < (c + 258)) // need to read more data first
@@ -576,7 +548,8 @@ int GIF_getInfo(GIFIMAGE *pPage, GIFINFO *pInfo)
memmove(cBuf, &cBuf[iOff], (iDataAvailable - iOff)); // move existing data down
iDataAvailable -= iOff;
iOff = 0;
iReadAmount = (*pPage->pfnRead)(&pPage->GIFFile, &cBuf[iDataAvailable], FILE_BUF_SIZE-iDataAvailable);
iReadAmount = (*pPage->pfnRead)(&pPage->GIFFile, &cBuf[iDataAvailable],
FILE_BUF_SIZE - iDataAvailable);
iDataAvailable += iReadAmount;
iDataRemaining -= iReadAmount;
// lFileOff += iReadAmount;
@@ -651,15 +624,14 @@ int GIF_getInfo(GIFIMAGE *pPage, GIFINFO *pInfo)
c = cBuf[iOff++]; /* Get length of next */
}
/* End of image data, check for more pages... */
if (cBuf[iOff] == 0x3b || (iDataRemaining == 0 && (iDataAvailable - iOff) < 32))
{
if (cBuf[iOff] == 0x3b || (iDataRemaining == 0 && (iDataAvailable - iOff) < 32)) {
bDone = 1; /* End of file has been reached */
}
else /* More pages to scan */
} else /* More pages to scan */
{
iNumFrames++;
// read new page data starting at this offset
if (pPage->GIFFile.iSize > FILE_BUF_SIZE && iDataRemaining > 0) // since we didn't read the whole file in one shot
if (pPage->GIFFile.iSize > FILE_BUF_SIZE
&& iDataRemaining > 0) // since we didn't read the whole file in one shot
{
memmove(cBuf, &cBuf[iOff], (iDataAvailable - iOff)); // move existing data down
iDataAvailable -= iOff;
@@ -687,15 +659,13 @@ gifpagesz:
// returns 1 to signify more data available for this image
// 0 indicates there is no more data
//
static int GIFGetMoreData(GIFIMAGE *pPage)
{
static int GIFGetMoreData(GIFIMAGE* pPage) {
int iDelta = (pPage->iLZWSize - pPage->iLZWOff);
unsigned char c = 1;
// move any existing data down
if (pPage->bEndOfFrame || iDelta >= (LZW_BUF_SIZE - MAX_CHUNK_SIZE) || iDelta <= 0)
return 1; // frame is finished or buffer is already full; no need to read more data
if (pPage->iLZWOff != 0)
{
if (pPage->iLZWOff != 0) {
// NB: memcpy() fails on some systems because the src and dest ptrs overlap
// so copy the bytes in a simple loop to avoid problems
for (int i = 0; i < pPage->iLZWSize - pPage->iLZWOff; i++) {
@@ -704,8 +674,7 @@ static int GIFGetMoreData(GIFIMAGE *pPage)
pPage->iLZWSize -= pPage->iLZWOff;
pPage->iLZWOff = 0;
}
while (c && pPage->GIFFile.iPos < pPage->GIFFile.iSize && pPage->iLZWSize < (LZW_BUF_SIZE-MAX_CHUNK_SIZE))
{
while (c && pPage->GIFFile.iPos < pPage->GIFFile.iSize && pPage->iLZWSize < (LZW_BUF_SIZE - MAX_CHUNK_SIZE)) {
(*pPage->pfnRead)(&pPage->GIFFile, &c, 1); // current length
(*pPage->pfnRead)(&pPage->GIFFile, &pPage->ucLZW[pPage->iLZWSize], c);
pPage->iLZWSize += c;
@@ -718,8 +687,7 @@ static int GIFGetMoreData(GIFIMAGE *pPage)
// Handle transparent pixels and disposal method
// Used only when a frame buffer is allocated
//
static void DrawNewPixels(GIFIMAGE *pPage, GIFDRAW *pDraw)
{
static void DrawNewPixels(GIFIMAGE* pPage, GIFDRAW* pDraw) {
uint8_t *d, *s;
int x, iPitch = pPage->iCanvasWidth;
@@ -733,16 +701,13 @@ static void DrawNewPixels(GIFIMAGE *pPage, GIFDRAW *pDraw)
if (pDraw->ucHasTransparency) // if transparency used
{
uint8_t c, ucTransparent = pDraw->ucTransparent;
for (x=0; x<pDraw->iWidth; x++)
{
for (x = 0; x < pDraw->iWidth; x++) {
c = *s++;
if (c != ucTransparent)
*d = c;
d++;
}
}
else
{
} else {
memcpy(d, s, pDraw->iWidth); // just overwrite the old pixels
}
} /* DrawNewPixels() */
@@ -751,30 +716,24 @@ static void DrawNewPixels(GIFIMAGE *pPage, GIFDRAW *pDraw)
// to either RGB565 or RGB888 output
// Used only when a frame buffer has been allocated
//
static void ConvertNewPixels(GIFIMAGE *pPage, GIFDRAW *pDraw)
{
static void ConvertNewPixels(GIFIMAGE* pPage, GIFDRAW* pDraw) {
uint8_t *d, *s;
int x;
s = &pPage->pFrameBuffer[(pPage->iCanvasWidth * (pDraw->iY + pDraw->y)) + pDraw->iX];
d = &pPage->pFrameBuffer[pPage->iCanvasHeight * pPage->iCanvasWidth]; // point past bottom of frame buffer
if (pPage->ucPaletteType == GIF_PALETTE_RGB565_LE || pPage->ucPaletteType == GIF_PALETTE_RGB565_BE)
{
if (pPage->ucPaletteType == GIF_PALETTE_RGB565_LE || pPage->ucPaletteType == GIF_PALETTE_RGB565_BE) {
uint16_t *pPal, *pu16;
pPal = (uint16_t*)pDraw->pPalette;
pu16 = (uint16_t*)d;
for (x=0; x<pPage->iWidth; x++)
{
for (x = 0; x < pPage->iWidth; x++) {
*pu16++ = pPal[*s++]; // convert to RGB565 pixels
}
}
else
{
} else {
uint8_t* pPal;
int pixel;
pPal = (uint8_t*)pDraw->pPalette;
for (x=0; x<pPage->iWidth; x++)
{
for (x = 0; x < pPage->iWidth; x++) {
pixel = *s++;
*d++ = pPal[(pixel * 3) + 0]; // convert to RGB888 pixels
*d++ = pPal[(pixel * 3) + 1];
@@ -786,8 +745,7 @@ static void ConvertNewPixels(GIFIMAGE *pPage, GIFDRAW *pDraw)
//
// GIFMakePels
//
static void GIFMakePels(GIFIMAGE *pPage, unsigned int code)
{
static void GIFMakePels(GIFIMAGE* pPage, unsigned int code) {
int iPixCount;
unsigned short* giftabs;
unsigned char *buf, *s, *pEnd, *gifpels;
@@ -798,8 +756,7 @@ static void GIFMakePels(GIFIMAGE *pPage, unsigned int code)
buf = pPage->ucLineBuf + (pPage->iWidth - pPage->iXCount);
giftabs = pPage->usGIFTable;
gifpels = &pPage->ucGIFPixels[PIXEL_LAST];
while (code < LINK_UNUSED)
{
while (code < LINK_UNUSED) {
if (s == pPage->ucFileBuf) /* Houston, we have a problem */
{
return; /* Exit with error */
@@ -809,15 +766,13 @@ static void GIFMakePels(GIFIMAGE *pPage, unsigned int code)
}
iPixCount = (int)(intptr_t)(pPage->ucFileBuf + FILE_BUF_SIZE - s);
while (iPixCount && pPage->iYCount > 0)
{
while (iPixCount && pPage->iYCount > 0) {
if (pPage->iXCount > iPixCount) /* Pixels fit completely on the line */
{
// memcpy(buf, s, iPixCount);
// buf += iPixCount;
pEnd = buf + iPixCount;
while (buf < pEnd)
{
while (buf < pEnd) {
*buf++ = *s++;
}
pPage->iXCount -= iPixCount;
@@ -825,13 +780,11 @@ static void GIFMakePels(GIFIMAGE *pPage, unsigned int code)
if (ucNeedMore)
GIFGetMoreData(pPage); // check if we need to read more LZW data every 4 lines
return;
}
else /* Pixels cross into next line */
} else /* Pixels cross into next line */
{
GIFDRAW gd;
pEnd = buf + pPage->iXCount;
while (buf < pEnd)
{
while (buf < pEnd) {
*buf++ = *s++;
}
iPixCount -= pPage->iXCount;
@@ -867,8 +820,7 @@ static void GIFMakePels(GIFIMAGE *pPage, unsigned int code)
if (pPage->pFrameBuffer) // update the frame buffer
{
DrawNewPixels(pPage, &gd);
if (pPage->ucDrawType == GIF_DRAW_COOKED)
{
if (pPage->ucDrawType == GIF_DRAW_COOKED) {
ConvertNewPixels(pPage, &gd); // prepare for output
gd.pPixels = &pPage->pFrameBuffer[pPage->iCanvasWidth * pPage->iCanvasHeight];
}
@@ -887,16 +839,20 @@ static void GIFMakePels(GIFIMAGE *pPage, unsigned int code)
//
// Macro to extract a variable length code
//
#define GET_CODE if (bitnum > (REGISTER_WIDTH - codesize)) { pImage->iLZWOff += (bitnum >> 3); \
bitnum &= 7; ulBits = INTELLONG(&p[pImage->iLZWOff]); } \
#define GET_CODE \
if (bitnum > (REGISTER_WIDTH - codesize)) { \
pImage->iLZWOff += (bitnum >> 3); \
bitnum &= 7; \
ulBits = INTELLONG(&p[pImage->iLZWOff]); \
} \
code = (unsigned short)(ulBits >> bitnum); /* Read a 32-bit chunk */ \
code &= sMask; bitnum += codesize;
code &= sMask; \
bitnum += codesize;
//
// Decode LZW into an image
//
static int DecodeLZW(GIFIMAGE *pImage, int iOptions)
{
static int DecodeLZW(GIFIMAGE* pImage, int iOptions) {
int i, bitnum;
unsigned short oldcode, codesize, nextcode, nextlim;
unsigned short *giftabs, cc, eoi;
@@ -925,8 +881,7 @@ static int DecodeLZW(GIFIMAGE *pImage, int iOptions)
// Initialize code table
// this part only needs to be initialized once
for (i = 0; i < cc; i++)
{
for (i = 0; i < cc; i++) {
gifpels[PIXEL_FIRST + i] = gifpels[PIXEL_LAST + i] = (unsigned short)i;
giftabs[i] = LINK_END;
}
@@ -947,13 +902,13 @@ init_codetable:
oldcode = code;
GIFMakePels(pImage, code); // first code is output as the first pixel
// Main decode loop
while (code != eoi && pImage->iYCount > 0) // && y < pImage->iHeight+1) /* Loop through all lines of the image (or strip) */
while (code != eoi
&& pImage->iYCount > 0) // && y < pImage->iHeight+1) /* Loop through all lines of the image (or strip) */
{
GET_CODE
if (code == cc) /* Clear code?, and not first code */
goto init_codetable;
if (code != eoi)
{
if (code != eoi) {
if (nextcode < nextlim) // for deferred cc case, don't let it overwrite the last entry (fff)
{
giftabs[nextcode] = oldcode;
@@ -964,8 +919,7 @@ init_codetable:
gifpels[PIXEL_LAST + nextcode] = gifpels[PIXEL_FIRST + code];
}
nextcode++;
if (nextcode >= nextlim && codesize < MAX_CODE_SIZE)
{
if (nextcode >= nextlim && codesize < MAX_CODE_SIZE) {
codesize++;
nextlim <<= 1;
sMask = (sMask << 1) | 1;
@@ -981,8 +935,7 @@ init_codetable:
// return -1;
} /* DecodeLZW() */
void GIF_setDrawCallback(GIFIMAGE *pGIF, GIF_DRAW_CALLBACK *pfnDraw)
{
void GIF_setDrawCallback(GIFIMAGE* pGIF, GIF_DRAW_CALLBACK* pfnDraw) {
pGIF->pfnDraw = pfnDraw;
} /* GIF_setDrawCallback() */
//
@@ -990,18 +943,19 @@ void GIF_setDrawCallback(GIFIMAGE *pGIF, GIF_DRAW_CALLBACK *pfnDraw)
// writes new values over previous line
// expects RGB565 little endian pixels as input
//
void GIF_scaleHalf(uint16_t *pCurrent, uint16_t *pPrev, int iWidth, int bBigEndian)
{
void GIF_scaleHalf(uint16_t* pCurrent, uint16_t* pPrev, int iWidth, int bBigEndian) {
int x;
uint16_t* d = pPrev;
uint32_t gSum, rbSum, pix0, pix1, pix2, pix3;
const uint32_t RBMask = 0xf81f, GMask = 0x7e0;
for (x=0; x<iWidth; x+=2)
{
pix0 = pCurrent[0]; pix1 = pCurrent[1];
pix2 = pPrev[0]; pix3 = pPrev[1];
pCurrent += 2; pPrev += 2;
for (x = 0; x < iWidth; x += 2) {
pix0 = pCurrent[0];
pix1 = pCurrent[1];
pix2 = pPrev[0];
pix3 = pPrev[1];
pCurrent += 2;
pPrev += 2;
gSum = (pix0 & GMask) + (pix1 & GMask) + (pix2 & GMask) + (pix3 & GMask);
gSum = ((gSum + 0x40) >> 2) & GMask; // for rounding towards 1
rbSum = (pix0 & RBMask) + (pix1 & RBMask) + (pix2 & RBMask) + (pix3 & RBMask);

View File

@@ -8,7 +8,6 @@
#include "lcd_api.h"
static const char* TAG = "lcd_api";
static DMA2D_HandleTypeDef hDma2dHandler2;
@@ -85,7 +84,12 @@ void lcd_task(void) {
}
}
void lcd_display_text(const char* text, uint32_t x_pos, uint32_t y_pos, uint32_t color, uint32_t bg_color, sFONT* font) {
void lcd_display_text(const char* text,
uint32_t x_pos,
uint32_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);
@@ -118,7 +122,12 @@ void lcd_display_text(const char* text, uint32_t x_pos, uint32_t y_pos, uint32_t
BSP_LCD_DisplayStringAt(x_pos, y_pos, (uint8_t*)text, LEFT_MODE);
}
void lcd_draw_raw_img(const void* p_src, uint32_t x_pos, uint32_t y_pos, uint32_t x_size, uint32_t y_size, uint32_t color_mode) {
void lcd_draw_raw_img(const void* p_src,
uint32_t x_pos,
uint32_t y_pos,
uint32_t x_size,
uint32_t y_size,
uint32_t color_mode) {
LOG_INFO(TAG, "Displaying raw image: @x=%lu, @y=%lu, width=%lu, height=%lu", x_pos, y_pos, x_size, y_size);
BSP_LCD_SelectLayer(0);
uint32_t address = hLtdcHandler.LayerCfg[1].FBStartAdress + (((BSP_LCD_GetXSize() * y_pos) + x_pos) * (4));

View File

@@ -10,8 +10,8 @@
#include <stdio.h>
#include <string.h>
#define LOGGER_LEVEL_WARN
#include "log.h"
#include "llfs.h"
#include "log.h"
/**
* @brief The maximum number of files that can be opened concurrently using the POSIX API
@@ -442,4 +442,3 @@ static uint8_t file_ext_cmp(const char* const filename, const char* const ext) {
}
return 1;
}

File diff suppressed because it is too large Load Diff

View File

@@ -7,8 +7,8 @@
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include "stm32f7xx_hal.h"
#include "log.h"
#include "stm32f7xx_hal.h"
extern UART_HandleTypeDef huart1;

View File

@@ -33,6 +33,7 @@
#include "modbus_tcp.h"
#include "UDP_broadcast.h"
#include "tcp_cmd.h"
#include "website_backend.h"
/* USER CODE END Includes */
@@ -88,8 +89,7 @@ static void MX_QUADSPI_Init(void);
* @brief The application entry point.
* @retval int
*/
int main(void)
{
int main(void) {
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
@@ -149,18 +149,21 @@ int main(void)
// 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()");
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");
}
/* Initialize website backend */
wbe_init();
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
while (1) {
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
@@ -174,8 +177,7 @@ int main(void)
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
void SystemClock_Config(void) {
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
@@ -199,29 +201,25 @@ void SystemClock_Config(void)
RCC_OscInitStruct.PLL.PLLN = 400;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
RCC_OscInitStruct.PLL.PLLQ = 2;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) {
Error_Handler();
}
/** Activate the Over-Drive mode
*/
if (HAL_PWREx_EnableOverDrive() != HAL_OK)
{
if (HAL_PWREx_EnableOverDrive() != HAL_OK) {
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_6) != HAL_OK)
{
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_6) != HAL_OK) {
Error_Handler();
}
}
@@ -231,8 +229,7 @@ void SystemClock_Config(void)
* @param None
* @retval None
*/
static void MX_DMA2D_Init(void)
{
static void MX_DMA2D_Init(void) {
/* USER CODE BEGIN DMA2D_Init 0 */
@@ -249,12 +246,10 @@ static void MX_DMA2D_Init(void)
hdma2d.LayerCfg[1].InputColorMode = DMA2D_INPUT_ARGB8888;
hdma2d.LayerCfg[1].AlphaMode = DMA2D_NO_MODIF_ALPHA;
hdma2d.LayerCfg[1].InputAlpha = 0;
if (HAL_DMA2D_Init(&hdma2d) != HAL_OK)
{
if (HAL_DMA2D_Init(&hdma2d) != HAL_OK) {
Error_Handler();
}
if (HAL_DMA2D_ConfigLayer(&hdma2d, 1) != HAL_OK)
{
if (HAL_DMA2D_ConfigLayer(&hdma2d, 1) != HAL_OK) {
Error_Handler();
}
/* USER CODE BEGIN DMA2D_Init 2 */
@@ -268,8 +263,7 @@ static void MX_DMA2D_Init(void)
* @param None
* @retval None
*/
static void MX_LTDC_Init(void)
{
static void MX_LTDC_Init(void) {
/* USER CODE BEGIN LTDC_Init 0 */
@@ -297,8 +291,7 @@ static void MX_LTDC_Init(void)
hltdc.Init.Backcolor.Blue = 0;
hltdc.Init.Backcolor.Green = 255;
hltdc.Init.Backcolor.Red = 0;
if (HAL_LTDC_Init(&hltdc) != HAL_OK)
{
if (HAL_LTDC_Init(&hltdc) != HAL_OK) {
Error_Handler();
}
pLayerCfg.WindowX0 = 0;
@@ -316,8 +309,7 @@ static void MX_LTDC_Init(void)
pLayerCfg.Backcolor.Blue = 0;
pLayerCfg.Backcolor.Green = 0;
pLayerCfg.Backcolor.Red = 0;
if (HAL_LTDC_ConfigLayer(&hltdc, &pLayerCfg, 0) != HAL_OK)
{
if (HAL_LTDC_ConfigLayer(&hltdc, &pLayerCfg, 0) != HAL_OK) {
Error_Handler();
}
pLayerCfg1.WindowX0 = 0;
@@ -335,8 +327,7 @@ static void MX_LTDC_Init(void)
pLayerCfg1.Backcolor.Blue = 0;
pLayerCfg1.Backcolor.Green = 0;
pLayerCfg1.Backcolor.Red = 0;
if (HAL_LTDC_ConfigLayer(&hltdc, &pLayerCfg1, 1) != HAL_OK)
{
if (HAL_LTDC_ConfigLayer(&hltdc, &pLayerCfg1, 1) != HAL_OK) {
Error_Handler();
}
/* USER CODE BEGIN LTDC_Init 2 */
@@ -350,8 +341,7 @@ static void MX_LTDC_Init(void)
* @param None
* @retval None
*/
static void MX_QUADSPI_Init(void)
{
static void MX_QUADSPI_Init(void) {
/* USER CODE BEGIN QUADSPI_Init 0 */
@@ -370,8 +360,7 @@ static void MX_QUADSPI_Init(void)
hqspi.Init.ClockMode = QSPI_CLOCK_MODE_0;
hqspi.Init.FlashID = QSPI_FLASH_ID_1;
hqspi.Init.DualFlash = QSPI_DUALFLASH_DISABLE;
if (HAL_QSPI_Init(&hqspi) != HAL_OK)
{
if (HAL_QSPI_Init(&hqspi) != HAL_OK) {
Error_Handler();
}
/* USER CODE BEGIN QUADSPI_Init 2 */
@@ -385,8 +374,7 @@ static void MX_QUADSPI_Init(void)
* @param None
* @retval None
*/
static void MX_USART1_UART_Init(void)
{
static void MX_USART1_UART_Init(void) {
/* USER CODE BEGIN USART1_Init 0 */
@@ -405,8 +393,7 @@ static void MX_USART1_UART_Init(void)
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
huart1.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
huart1.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
if (HAL_UART_Init(&huart1) != HAL_OK)
{
if (HAL_UART_Init(&huart1) != HAL_OK) {
Error_Handler();
}
/* USER CODE BEGIN USART1_Init 2 */
@@ -416,8 +403,7 @@ static void MX_USART1_UART_Init(void)
}
/* FMC initialization function */
static void MX_FMC_Init(void)
{
static void MX_FMC_Init(void) {
/* USER CODE BEGIN FMC_Init 0 */
@@ -452,8 +438,7 @@ static void MX_FMC_Init(void)
SdramTiming.RPDelay = 16;
SdramTiming.RCDDelay = 16;
if (HAL_SDRAM_Init(&hsdram1, &SdramTiming) != HAL_OK)
{
if (HAL_SDRAM_Init(&hsdram1, &SdramTiming) != HAL_OK) {
Error_Handler();
}
@@ -467,8 +452,7 @@ static void MX_FMC_Init(void)
* @param None
* @retval None
*/
static void MX_GPIO_Init(void)
{
static void MX_GPIO_Init(void) {
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* USER CODE BEGIN MX_GPIO_Init_1 */
/* USER CODE END MX_GPIO_Init_1 */
@@ -523,13 +507,11 @@ static void MX_GPIO_Init(void)
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
void Error_Handler(void) {
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
while (1) {
}
/* USER CODE END Error_Handler_Debug */
}

View File

@@ -1,163 +1,594 @@
/**
* @file modbus_tcp.c
*
* @brief TCP Modbus handler
* @date Nov 6, 2023
* @brief TCP Modbus server
* @date Nov 29, 2023
* @author Obe
* @author Lorenz C.
*/
// Includes
#include "modbus_tcp.h"
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <tcp.h>
#include "lcd_api.h"
#include "llfs.h"
#define LOGGER_LEVEL_ALL
#include "log.h"
#include "modbus_tcp.h"
// Defines
#define MAX_REG REG_LENGTH
#define EXTENSION_LENGTH 4
#define TEXT_LENGTH 200
#define MULTIPLE_REG 0x10
#define REG_LENGTH 428
#define START_DATA 28
#define MODBUS_MODE 7
// TCP server constants
#define TCP_POLL_INTERVAL 10 // About 5 seconds
#define REG_COLOR_B_RED 14
#define REG_COLOR_B_GREEN 16
#define REG_COLOR_B_BLUE 18
// Modbus constants (See Modbus_Application_Protocol_V1_1b3 and Modbus_Messaging_Implementation_Guide_V1_0b)
#define PDU_MAX_LENGTH 253
#define ADU_MAX_LENGTH 260
#define REG_COLOR_F_RED 20
#define REG_COLOR_F_GREEN 22
#define REG_COLOR_F_BLUE 24
#define MBAP_HEADER_LENGTH 7
#define PROTOCOL_ID_MODBUS 0x0000
#define REG_IMAGE_NR 26
#define WRITE_MULTIPLE_REG_REQ_MIN_LENGTH 5
#define WRITE_MULTIPLE_REG_RSP_LENGTH 4
#define WRITE_MULTIPLE_REG_QUANTITY_MIN 0x0001
#define WRITE_MULTIPLE_REG_QUANTITY_MAX 0x007B // See m
// Global variables
static char* TAG = "Modbus_TCP"; // Tag used in logs
#define EXCEPTION_OFFSET 0x80
static struct tcp_pcb* modbus_pcb;
uint8_t registers[MAX_REG];
// Functions
static err_t modbus_incoming_data(void* arg, struct tcp_pcb* pcb, struct pbuf* p, err_t err);
static err_t modbus_accept(void* arg, struct tcp_pcb* pcb, err_t err);
// Application specific constants
#define REGISTER_COUNT 208
#define REG_ADDR_BG_COLOR_RED 0x0000 // 8-bit red background color
#define REG_ADDR_BG_COLOR_GREEN 0x0001 // 8-bit green background color
#define REG_ADDR_BG_COLOR_BLUE 0x0002 // 8-bit blue background colo
#define REG_ADDR_FG_COLOR_RED 0x0003 // 8-bit red text color
#define REG_ADDR_FG_COLOR_GREEN 0x0004 // 8-bit green text color
#define REG_ADDR_FG_COLOR_BLUE 0x0005 // 8-bit blue text color
#define REG_ADDR_IMAGE_NUM 0x0006 // 16-bit image number
#define REG_ADDR_TEXT 0x0007 // Start of text registers (1 reg / ascii character, null terminated)
#define REG_SIZE_TEXT 0x00C8 // 200 registers / characters
#define TEXT_POS_X 10
#define TEXT_POS_Y 10
#define IMG_POS_X 0
#define IMG_POS_Y 75
/**
* @fn static err_t modbus_incoming_data(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
* @brief Function that's called when there is a new request on port 502.
* It handles the incoming data from QModMaster
* @brief Error codes for internal use in the modbus tcp server.
*/
static err_t modbus_incoming_data(void* arg, struct tcp_pcb* pcb, struct pbuf* p, err_t err) {
uint8_t counter;
char text[TEXT_LENGTH];
uint32_t result_background = 0xff000000;
uint32_t text_foreground_color = 0xff000000;
typedef enum {
MB_TCP_ERR_OK,
MB_TCP_ERR_FAILED,
MB_TCP_ERR_INVALID_ADU,
MB_TCP_ERR_INVALID_PROTOCOL_ID,
MB_TCP_ERR_INVALID_LENGTH,
MB_TCP_ERR_MEM,
} mb_tcp_err_t;
LWIP_UNUSED_ARG(arg); // This is used to prevent a warning
/**
* @brief Modbus function codes
*/
enum {
WRITE_MULTIPLE_REGISTERS = 0x10,
};
// Putting underscores in the whole array
memset(text, '_', TEXT_LENGTH);
text[TEXT_LENGTH - 1] = '\0';
/**
* @brief Modbus exception codes
*/
typedef enum {
ILLEGAL_FUNCTION = 0x01,
ILLEGAL_DATA_ADDRESS = 0x02,
ILLEGAL_DATA_VALUE = 0x03,
SERVER_DEVICE_FAILURE = 0x04,
ACKNOWLEDGE = 0x05,
SERVER_DEVICE_BUSY = 0x06,
MEMORY_PARITY_ERROR = 0x08,
GATEWAY_PATH_UNAVAILABLE = 0x0A,
GATEWAY_TARGET_DEVICE_FAILED_TO_RESPOND = 0x0B,
} mb_exception_code_t;
if (p != NULL) {
LOG_INFO(TAG, "data is valid\n");
// Process the modbus data
/**
* @brief Modbus TCP Application Data Unit (ADU)
*/
typedef struct {
struct {
uint16_t transaction_id;
uint16_t protocol_id;
uint16_t length;
uint8_t unit_id;
} mbap_header;
uint8_t function_code;
uint8_t* data;
} modbus_tcp_t;
/**
* @brief The data fields of the write multiple registers request PDU.
* @note The data field is not included in the struct.
*/
typedef struct {
uint16_t start_address;
uint16_t quantity;
uint8_t byte_count;
} write_multiple_reg_req_t;
// Static global variables
static char* TAG = "Modbus_TCP"; // Tag used in logs
static uint16_t registers[REGISTER_COUNT]; // The modbus registers
// Function prototypes
static err_t tcp_accept_cb(void* arg, struct tcp_pcb* new_pcb, err_t err);
static void tcp_err_cb(void* arg, err_t err);
static err_t tcp_poll_cb(void* arg, struct tcp_pcb* pcb);
static err_t tcp_sent_cb(void* arg, struct tcp_pcb* pcb, u16_t len);
static err_t tcp_recv_cb(void* arg, struct tcp_pcb* pcb, struct pbuf* p, err_t err);
static mb_tcp_err_t parse_data_to_adu(modbus_tcp_t* adu, uint8_t* data, size_t length);
static mb_tcp_err_t handle_modbus_request(modbus_tcp_t* req_adu, modbus_tcp_t* rsp_adu);
static mb_tcp_err_t send_modbus_response(struct tcp_pcb* pcb, modbus_tcp_t* rsp_adu);
static void handle_mb_func_write_multiple_req(modbus_tcp_t* req_adu, modbus_tcp_t* rsp_adu);
static void generate_modbus_exception_rsp(modbus_tcp_t* req_adu,
modbus_tcp_t* rsp_adu,
mb_exception_code_t exception_code);
static void modbus_update_app(void);
static const char* img_num_to_filename(uint16_t img_num);
static void dump_adu(modbus_tcp_t* adu);
void modbus_tcp_init(void) {
struct tcp_pcb* modbus_pcb;
// Initialize the modbus tcp pcb
modbus_pcb = tcp_new();
if (modbus_pcb == NULL) {
LOG_CRIT(TAG, "Failed to create modbus pcb");
return;
}
// Listen on all interfaces (port 502)
if (tcp_bind(modbus_pcb, IP_ADDR_ANY, MODBUS_TCP_PORT) != ERR_OK) {
LOG_CRIT(TAG, "Failed to bind modbus pcb");
return;
}
// Set the state of the pcb to LISTEN
modbus_pcb = tcp_listen(modbus_pcb);
if (modbus_pcb == NULL) {
LOG_CRIT(TAG, "Failed to listen on modbus pcb");
return;
}
// Set the callback function for incoming connections
tcp_accept(modbus_pcb, tcp_accept_cb);
}
/**
* @brief Callback function for incoming connections.
*
* @param arg not used
* @param new_pcb
* @param err
* @return
*/
static err_t tcp_accept_cb(void* arg, struct tcp_pcb* new_pcb, err_t err) {
LOG_DEBUG(TAG, "TCP accept");
if (err != ERR_OK) {
LOG_WARN(TAG, "TCP accept failed with error(%d): %s", err, lwip_strerr(err));
return err;
}
// Set the callback functions for the new pcb
tcp_recv(new_pcb, tcp_recv_cb);
tcp_sent(new_pcb, tcp_sent_cb);
tcp_err(new_pcb, tcp_err_cb);
tcp_poll(new_pcb, tcp_poll_cb, TCP_POLL_INTERVAL);
return ERR_OK;
}
/**
* @brief Callback function for tcp errors.
*
* @param arg
* @param err
*/
static void tcp_err_cb(void* arg, err_t err) {
LOG_WARN(TAG, "TCP error(%d): %s", err, lwip_strerr(err));
}
/**
* @brief Callback function for tcp poll.
*
* This function is called periodically to check if the connection is still alive.
* The interval is set by TCP_POLL_INTERVAL.
*
* @param arg
* @param pcb
* @return ERR_OK
*/
static err_t tcp_poll_cb(void* arg, struct tcp_pcb* pcb) {
LOG_DEBUG(TAG, "TCP poll");
return ERR_OK;
}
/**
* @brief Callback function for tcp sent.
*
* Called when sent data has been acknowledged by the remote side.
*
* @param arg
* @param pcb
* @param len
* @return ERR_OK
*/
static err_t tcp_sent_cb(void* arg, struct tcp_pcb* pcb, u16_t len) {
LOG_DEBUG(TAG, "TCP data acknowledged");
return ERR_OK;
}
/**
* @brief Callback function for tcp receive.
*
* Called when data has been received.
*
* @param arg
* @param pcb
* @param p
* @param err
* @return
*/
static err_t tcp_recv_cb(void* arg, struct tcp_pcb* pcb, struct pbuf* p, err_t err) {
modbus_tcp_t mb_req_adu; // Modbus request adu to store the received data in
LOG_DEBUG(TAG, "TCP data received");
// Connection closed?
if (p == NULL && err == ERR_OK) {
LOG_INFO(TAG, "Remote closed connection");
return tcp_close(pcb);
}
if (err != ERR_OK) {
LOG_WARN(TAG, "TCP data received with error(%d): %s", err, lwip_strerr(err));
return ERR_OK;
}
// Copy the data from the pbuf to the modbus request adu
mb_tcp_err_t mb_err = parse_data_to_adu(&mb_req_adu, p->payload, p->len);
if (mb_err != MB_TCP_ERR_OK) {
LOG_WARN(TAG, "Invalid modbus adu received");
goto err_adu_read;
}
// Handle the modbus request
modbus_tcp_t mb_rsp_adu;
handle_modbus_request(&mb_req_adu, &mb_rsp_adu);
// Tell the tcp stack that we have taken the data
tcp_recved(pcb, p->tot_len);
// Putting the buffer in the register array
for (uint16_t i = 0; i < p->tot_len && i < MAX_REG; i++) {
registers[i] = ((uint8_t*)p->payload)[i];
// Send the modbus response
if (send_modbus_response(pcb, &mb_rsp_adu) != MB_TCP_ERR_OK) {
LOG_WARN(TAG, "Failed to send modbus response");
goto err_rsp_fail;
}
if (registers[MODBUS_MODE] == MULTIPLE_REG) {
// Check if it's a Modbus Write Multiple Registers request (0x10)
LOG_INFO(TAG, "in writing multiple register mode\n");
err_rsp_fail:
free(mb_rsp_adu.data);
err_adu_read:
pbuf_free(p);
return ERR_OK;
}
LOG_INFO(TAG, "Background R:%d G:%d B:%d\nForeground: R:%d G:%d B:%d\nImage Nr: %d",
registers[REG_COLOR_B_RED], registers[REG_COLOR_B_GREEN], registers[REG_COLOR_B_BLUE],
registers[REG_COLOR_F_RED], registers[REG_COLOR_F_GREEN], registers[REG_COLOR_F_BLUE],
registers[REG_IMAGE_NR]);
/**
* @brief Parses the given data into the tcp ADU struct.
*
* This function takes the raw data received and parses it into a modbus TCP Application Data Unit (ADU).
*
* @note The data field of the ADU still points to the raw data, so it must stay valid until the ADU is no longer
* needed.
* @todo Store the data in the ADU struct instead of just pointing to it?
*
* @param[out] adu Pointer to a modbus_tcp_t structure where the parsed ADU will be stored.
* @param[in] data Pointer to the raw data received from the modbus TCP server.
* @param[in] length Length of the raw data.
*/
static mb_tcp_err_t parse_data_to_adu(modbus_tcp_t* adu, uint8_t* data, size_t length) {
if (length > ADU_MAX_LENGTH) {
LOG_DEBUG(TAG, "Invalid adu length: %d, expected max %d", length, ADU_MAX_LENGTH);
return MB_TCP_ERR_INVALID_ADU;
}
if (length < MBAP_HEADER_LENGTH) {
LOG_DEBUG(TAG, "Invalid adu length: %d, expected at least %d", length, MBAP_HEADER_LENGTH);
return MB_TCP_ERR_INVALID_ADU;
}
counter = 0;
for (int i = START_DATA; i < REG_LENGTH; i++) {
if (i % 2 == 0) {
text[counter] = registers[i];
counter++;
// The adu struct is a one-to-one map of the modbus adu, so we can just copy the data
// But modbus fields are big endian, so we need to convert them to little endian
adu->mbap_header.transaction_id = (data[0] << 8) | data[1];
adu->mbap_header.protocol_id = (data[2] << 8) | data[3];
adu->mbap_header.length = (data[4] << 8) | data[5];
adu->mbap_header.unit_id = data[6];
adu->function_code = data[7];
adu->data = &data[8]; // Don't change the data endianness yet, since it's structure is function dependent
// Correct protocol id?
if (adu->mbap_header.protocol_id != PROTOCOL_ID_MODBUS) {
LOG_DEBUG(TAG, "Invalid protocol id: %d, expected %d", adu->mbap_header.protocol_id, PROTOCOL_ID_MODBUS);
return MB_TCP_ERR_INVALID_PROTOCOL_ID;
}
// Length matches length field?
if (adu->mbap_header.length != length - MBAP_HEADER_LENGTH + 1) {
LOG_DEBUG(TAG, "Length mismatch: %d, expected %d", adu->mbap_header.length, length - MBAP_HEADER_LENGTH + 1);
return MB_TCP_ERR_INVALID_ADU;
}
return MB_TCP_ERR_OK;
}
/**
* @brief Handles the given modbus request and generates a response.
*
* Handles the given modbus request and generates a response, either a normal response or an exception response.
* The response data field is allocated and must be freed by the caller.
*
* @param[in] req_adu Pointer to the modbus request adu.
* @param[out] rsp_adu Pointer to the modbus response adu.
* @return MB_TCP_ERR_OK if the request was handled successfully, otherwise an error code.
*/
static mb_tcp_err_t handle_modbus_request(modbus_tcp_t* req_adu, modbus_tcp_t* rsp_adu) {
// Check if the function code is supported
switch (req_adu->function_code) {
case WRITE_MULTIPLE_REGISTERS: {
LOG_INFO(TAG, "Write multiple registers request received");
handle_mb_func_write_multiple_req(req_adu, rsp_adu);
break;
}
default: {
LOG_WARN(TAG, "Unsupported function code: %d", req_adu->function_code);
generate_modbus_exception_rsp(req_adu, rsp_adu, ILLEGAL_FUNCTION);
}
}
result_background |= ((uint32_t)registers[REG_COLOR_B_RED]) << 16;
result_background |= ((uint32_t)registers[REG_COLOR_B_GREEN]) << 8;
result_background |= (uint32_t)registers[REG_COLOR_B_BLUE];
return MB_TCP_ERR_OK;
}
text_foreground_color |= ((uint32_t)registers[REG_COLOR_F_RED]) << 16;
text_foreground_color |= ((uint32_t)registers[REG_COLOR_F_GREEN]) << 8;
text_foreground_color |= (uint32_t)registers[REG_COLOR_F_BLUE];
/**
* @brief Generates a modbus exception response.
*
* Generates a modbus exception response based on the given request adu and exception code.
* The response data field is allocated and must be freed by the caller.
*
* @param[in] req_adu The request adu to base the response adu on.
* @param[out] rsp_adu The response adu to fill.
* @param[in] exception_code The exception code to use.
*/
static void generate_modbus_exception_rsp(modbus_tcp_t* req_adu,
modbus_tcp_t* rsp_adu,
mb_exception_code_t exception_code) {
uint16_t pdu_length = 2; // Function code + exception code
// Processing the image index
size_t number_of_files = llfs_file_count(); // How many files that there are
// Fill the response adu based on the request adu
rsp_adu->mbap_header.transaction_id = req_adu->mbap_header.transaction_id;
rsp_adu->mbap_header.protocol_id = PROTOCOL_ID_MODBUS;
rsp_adu->mbap_header.length = 1 + pdu_length; // 1 for the unit id
rsp_adu->mbap_header.unit_id = req_adu->mbap_header.unit_id;
rsp_adu->function_code = req_adu->function_code + EXCEPTION_OFFSET;
if (number_of_files > 0) {
llfs_file_t file_list[number_of_files];
number_of_files = llfs_file_list(file_list, number_of_files, NULL);
// Allocate memory for the exception code
rsp_adu->data = malloc(1);
if (rsp_adu->data == NULL) {
LOG_CRIT(TAG, "Failed to allocate memory for exception code");
return;
}
lcd_clear_text();
rsp_adu->data[0] = exception_code;
}
/**
* @brief Sends the given modbus response.
*
* Sends the given modbus response to the given tcp pcb.
* A copy of the response data is made, so the response adu data can be freed after this function returns.
*
* @param[in,out] pcb The tcp pcb to send the response to (same as the pcb used to receive the request).
* @param[in] rsp_adu The response adu to send.
* @return MB_TCP_ERR_OK if the response was sent successfully, otherwise an error code.
*/
static mb_tcp_err_t send_modbus_response(struct tcp_pcb* pcb, modbus_tcp_t* rsp_adu) {
uint16_t pdu_length = rsp_adu->mbap_header.length - 1; // Length of the data + 1 (for the unit id)
uint16_t adu_length = MBAP_HEADER_LENGTH + pdu_length;
uint8_t data[adu_length];
err_t err;
if (pdu_length > PDU_MAX_LENGTH) {
LOG_WARN(TAG, "Invalid pdu length: %d, expected less than %d", pdu_length, PDU_MAX_LENGTH);
return MB_TCP_ERR_INVALID_ADU;
}
LOG_DEBUG(TAG, "Sending modbus response with length: %d", adu_length);
// Serialize the adu (little endian -> big endian)
data[0] = rsp_adu->mbap_header.transaction_id >> 8;
data[1] = rsp_adu->mbap_header.transaction_id & 0xFF;
data[2] = rsp_adu->mbap_header.protocol_id >> 8;
data[3] = rsp_adu->mbap_header.protocol_id & 0xFF;
data[4] = rsp_adu->mbap_header.length >> 8;
data[5] = rsp_adu->mbap_header.length & 0xFF;
data[6] = rsp_adu->mbap_header.unit_id;
data[7] = rsp_adu->function_code;
// The data should already be in big endian, so we can just copy it
memcpy(&data[8], rsp_adu->data, pdu_length - 1); // -1 function code is also in the pdu
if (adu_length > tcp_sndbuf(pcb)) {
LOG_WARN(TAG, "Not enough space in tcp buffer to send modbus response");
return MB_TCP_ERR_MEM;
}
// Send the data
err = tcp_write(pcb, data, adu_length, TCP_WRITE_FLAG_COPY);
if (err != ERR_OK) {
LOG_WARN(TAG, "Failed to send modbus response with error(%d): %s", err, lwip_strerr(err));
return MB_TCP_ERR_FAILED;
}
return MB_TCP_ERR_OK;
}
/**
* @brief Handles a write multiple registers request.
*
* Handles a write multiple registers request and generates a response adu accordingly.
*
* @param[in] req_adu The request adu to handle.
* @param[out] rsp_adu The response adu to fill.
*/
static void handle_mb_func_write_multiple_req(modbus_tcp_t* req_adu, modbus_tcp_t* rsp_adu) {
write_multiple_reg_req_t req_pdu;
uint16_t req_data_length = req_adu->mbap_header.length - 2; // -2 for the unit id and function code
// Request at least enough data for the minimum length?
if (req_data_length < WRITE_MULTIPLE_REG_REQ_MIN_LENGTH) {
LOG_WARN(TAG, "Invalid write multiple registers request length, not enough data for minimum length");
generate_modbus_exception_rsp(req_adu, rsp_adu, ILLEGAL_DATA_VALUE);
return;
}
// Map the data to the write multiple registers request struct and convert it to little endian
req_pdu.start_address = (req_adu->data[0] << 8) | req_adu->data[1];
req_pdu.quantity = (req_adu->data[2] << 8) | req_adu->data[3];
req_pdu.byte_count = req_adu->data[4];
// Request the correct length? Do the byte and register count match?
if (req_data_length != WRITE_MULTIPLE_REG_REQ_MIN_LENGTH + req_pdu.byte_count
|| req_pdu.quantity < WRITE_MULTIPLE_REG_QUANTITY_MIN || req_pdu.quantity > WRITE_MULTIPLE_REG_QUANTITY_MAX
|| req_pdu.quantity * 2 != req_pdu.byte_count) {
LOG_WARN(TAG, "Invalid write multiple registers request length");
generate_modbus_exception_rsp(req_adu, rsp_adu, ILLEGAL_DATA_VALUE);
return;
}
// Invalid start address or quantity?
if (req_pdu.start_address + req_pdu.quantity >= REGISTER_COUNT) {
LOG_DEBUG(TAG, "Invalid start address or quantity");
generate_modbus_exception_rsp(req_adu, rsp_adu, ILLEGAL_DATA_ADDRESS);
return;
}
// Convert the data to register values (big endian -> little endian)
for (uint16_t i = 0; i < req_pdu.quantity; i++) {
registers[req_pdu.start_address + i] = (req_adu->data[WRITE_MULTIPLE_REG_REQ_MIN_LENGTH + i * 2] << 8)
| req_adu->data[WRITE_MULTIPLE_REG_REQ_MIN_LENGTH + 1 + i * 2];
}
// Update the application with the new register values
modbus_update_app(); // TODO: do this when the request is handled successfully, to avoid timeouts.
// Fill the response adu based on the request adu
rsp_adu->mbap_header.transaction_id = req_adu->mbap_header.transaction_id;
rsp_adu->mbap_header.protocol_id = PROTOCOL_ID_MODBUS;
rsp_adu->mbap_header.length = 2 + WRITE_MULTIPLE_REG_RSP_LENGTH; // 2 for the unit id and function code
rsp_adu->mbap_header.unit_id = req_adu->mbap_header.unit_id;
rsp_adu->function_code = req_adu->function_code;
// Allocate memory for the response data
rsp_adu->data = malloc(WRITE_MULTIPLE_REG_RSP_LENGTH);
if (rsp_adu->data == NULL) {
LOG_CRIT(TAG, "Failed to allocate memory for response data");
return;
}
// The response data are the same 4 bytes as the request data
memcpy(rsp_adu->data, req_adu->data, WRITE_MULTIPLE_REG_RSP_LENGTH);
}
/**
* @brief Dumps the given ADU to the log.
*
* @param[in] adu Pointer to the ADU to dump.
*/
static void dump_adu(modbus_tcp_t* adu) {
LOG_DEBUG(TAG, "Modbus adu:");
LOG_DEBUG(TAG, " Transaction id: %d", adu->mbap_header.transaction_id);
LOG_DEBUG(TAG, " Protocol id: %d", adu->mbap_header.protocol_id);
LOG_DEBUG(TAG, " Length: %d", adu->mbap_header.length);
LOG_DEBUG(TAG, " Unit id: %d", adu->mbap_header.unit_id);
LOG_DEBUG(TAG, " Function code: %d", adu->function_code);
LOG_DEBUG(TAG, " Data: ");
for (size_t i = 0; i < adu->mbap_header.length - 2; i++) {
LOG_DEBUG(TAG, " [%d]:%d", i, adu->data[i]);
}
}
/**
* @brief Update the application with the new register values
*/
static void modbus_update_app(void) {
uint32_t text_color; // Text color in ARGB888
uint32_t bg_color; // Background color in ARGB888
char text[REG_SIZE_TEXT];
const char* filename;
LOG_INFO(TAG, "Updating application with new register values");
// Get the colors from the registers
text_color = 0xFF000000 | (registers[REG_ADDR_FG_COLOR_RED] << 16) | (registers[REG_ADDR_FG_COLOR_GREEN] << 8)
| registers[REG_ADDR_FG_COLOR_BLUE];
bg_color = 0xFF000000 | (registers[REG_ADDR_BG_COLOR_RED] << 16) | (registers[REG_ADDR_BG_COLOR_GREEN] << 8)
| registers[REG_ADDR_BG_COLOR_BLUE];
// Get the text from the registers
for (int i = 0; i < REG_SIZE_TEXT; i++) {
text[i] = registers[REG_ADDR_TEXT + i];
}
// Get the filename based on the image number register
filename = img_num_to_filename(registers[REG_ADDR_IMAGE_NUM]);
// Clear the screen
lcd_clear_images();
lcd_stop_all_gifs();
lcd_clear_text();
lcd_display_text(text, 10, 10, text_foreground_color, result_background, LCD_FONT24);
// Display the text
lcd_display_text(text, TEXT_POS_X, TEXT_POS_Y, text_color, bg_color, LCD_FONT24);
if (number_of_files < registers[REG_IMAGE_NR]) {
lcd_display_text("FILE NOT IN FILESYSTEM", 10, 75, LCD_RED, LCD_BLACK, LCD_FONT24);
} else {
const char* ext = strrchr(file_list[registers[REG_IMAGE_NR] - 1].name, '.');
// Try to display the image
if (filename != NULL) {
LOG_DEBUG(TAG, "Displaying image: %s", filename);
char* ext = strrchr(filename, '.');
if (ext == NULL) {
LOG_CRIT(TAG, "No valid extension found");
LOG_WARN(TAG, "File %s has no valid extension", filename);
} else if (strcmp(ext, ".gif") == 0) {
lcd_draw_gif_from_llfs_file(&file_list[registers[REG_IMAGE_NR] - 1], 0, 75);
lcd_draw_gif_from_fs(filename, IMG_POS_X, IMG_POS_Y);
} else if (strcmp(ext, ".bmp") == 0) {
lcd_draw_img_from_llfs_file(&file_list[registers[REG_IMAGE_NR] - 1], 0, 75);
}
}
}
lcd_draw_img_from_fs(filename, IMG_POS_X, IMG_POS_Y);
} else {
LOG_INFO(TAG, "not in writing multiple register mode!!!\n");
LOG_WARN(TAG, "File %s is not a valid img", filename);
}
} else if (err == ERR_OK) {
tcp_close(pcb); // When everything was ok close the TCP connection
} else {
LOG_WARN(TAG, "No image found");
}
return ERR_OK;
}
/**
* @fn static err_t modbus_accept(void *arg, struct tcp_pcb *pcb, err_t err)
* @brief Sets the function that's being called when theirs incoming data
* @brief Convert the image number register to a filename
*
* Converts the image number register to a filename by looking up the file in the filesystem.
*
* @note This function doesn't check if the file is a valid image file.
* So the image number is more a file number.
*
* @param[in] img_num The image number register
* @return The filename of the image or NULL if no file is found
*/
static err_t modbus_accept(void* arg, struct tcp_pcb* pcb, err_t err) {
LWIP_UNUSED_ARG(arg);
LWIP_UNUSED_ARG(err);
static const char* img_num_to_filename(uint16_t img_num) {
size_t number_of_files = llfs_file_count();
// Sets the priority of a connection.
tcp_setprio(pcb, TCP_PRIO_MIN);
LOG_DEBUG(TAG, "Converting image number %d to filename, %d files found", img_num, number_of_files);
// Sets which function is being called when new data arrives
tcp_recv(pcb, modbus_incoming_data);
return ERR_OK;
if (number_of_files == 0 || img_num > number_of_files) {
LOG_DEBUG(TAG, "No files found or invalid image number: %d", img_num);
return NULL;
}
/**
* @fn void modbus_init
* @brief Initializes the modbus tcp
*/
void modbus_init(void) {
LOG_INFO(TAG, "Initializing");
// Creating a new tcp pcb
modbus_pcb = tcp_new();
// Bind the modbus_pcb to port 502
tcp_bind(modbus_pcb, IP_ADDR_ANY, MODBUSPORT);
modbus_pcb = tcp_listen(modbus_pcb);
// Set callback function for incoming connections
tcp_accept(modbus_pcb, modbus_accept);
LOG_INFO(TAG, "initialized");
llfs_file_t files[number_of_files];
llfs_file_list(files, number_of_files, NULL);
return files[img_num].name;
}

View File

@@ -28,13 +28,7 @@
#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;
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);
@@ -47,7 +41,8 @@ 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
// 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;
@@ -82,7 +77,8 @@ static void publish_data(mqtt_client_t* client, void* arg) {
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);
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);
}
@@ -90,7 +86,8 @@ static void publish_data(mqtt_client_t* client, void* arg) {
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);
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);
}

View File

@@ -36,7 +36,6 @@ static err_t tcp_cmd_recv(void* arg, struct tcp_pcb* pcb, struct pbuf* p, err_t
char* endptr;
if (err == ERR_OK && p != NULL) {
tcp_recved(pcb, p->tot_len);
pc = (char*)p->payload;
@@ -48,13 +47,15 @@ static err_t tcp_cmd_recv(void* arg, struct tcp_pcb* pcb, struct pbuf* p, err_t
if (!strncmp(tcp_buffer, "help", 4)) {
check = 1;
tcp_write(pcb, "help : laat lijst zien met alle commando's\r\n"
tcp_write(pcb,
"help : laat lijst zien met alle commando's\r\n"
"text : geeft tekst mee die op LCD komt (uw_text)\r\n"
"color : kleur achtergrond van scherm (255 255 255)\r\n"
"textColor : kleur van tekst (255 255 255)\r\n"
"listImages: laat een lijst zien van de mogelijke afbeeldingen\r\n"
"setImage : veranderd te afbeelding (naam_afbeelding)\r\n"
"exit : sluit de verbinding\r\n", 354, TCP_WRITE_FLAG_COPY | TCP_WRITE_FLAG_MORE);
"exit : sluit de verbinding\r\n",
354, TCP_WRITE_FLAG_COPY | TCP_WRITE_FLAG_MORE);
} else if (!strncmp(tcp_buffer, "text ", 5)) {
size_t i;
for (i = 0; i < len - 4; i++) {
@@ -64,7 +65,6 @@ static err_t tcp_cmd_recv(void* arg, struct tcp_pcb* pcb, struct pbuf* p, err_t
lcd_clear_text();
lcd_display_text(text, 10, 10, result_txt, result_bg, LCD_FONT24);
check = 1;
} else if (!strncmp(tcp_buffer, "color", 5)) {
for (size_t i = 0; i < 3; i++) {
@@ -96,7 +96,8 @@ static err_t tcp_cmd_recv(void* arg, struct tcp_pcb* pcb, struct pbuf* p, err_t
number_of_files = llfs_file_list(file_list, number_of_files, NULL);
for (size_t i = 0; i < number_of_files; i++) {
tcp_write(pcb, file_list[i].name, strlen(file_list[i].name), TCP_WRITE_FLAG_COPY | TCP_WRITE_FLAG_MORE);
tcp_write(pcb, file_list[i].name, strlen(file_list[i].name),
TCP_WRITE_FLAG_COPY | TCP_WRITE_FLAG_MORE);
tcp_write(pcb, "\r\n", 2, TCP_WRITE_FLAG_COPY | TCP_WRITE_FLAG_MORE);
}
} else {
@@ -154,7 +155,8 @@ static err_t tcp_cmd_recv(void* arg, struct tcp_pcb* pcb, struct pbuf* p, err_t
}
if (!check && (strncmp(tcp_buffer, "\r\n", 2) != 0)) {
tcp_write(pcb, "Onbestaand commando: help voor lijst van commando's\r\n", 53, TCP_WRITE_FLAG_COPY | TCP_WRITE_FLAG_MORE);
tcp_write(pcb, "Onbestaand commando: help voor lijst van commando's\r\n", 53,
TCP_WRITE_FLAG_COPY | TCP_WRITE_FLAG_MORE);
}
pbuf_free(p);
@@ -184,10 +186,12 @@ static err_t tcp_cmd_accept(void* arg, struct tcp_pcb* pcb, err_t err) {
tcp_recv(pcb, tcp_cmd_recv);
tcp_err(pcb, NULL);
tcp_poll(pcb, NULL, 4);
tcp_write(pcb, " Welcom bij de TCP CMD Interface\r\n"
tcp_write(pcb,
" Welcom bij de TCP CMD Interface\r\n"
"(Typ help voor een lijst van de commando's! X om te sluiten)\r\n"
"============================================================\r\n"
"User: ", 168, TCP_WRITE_FLAG_COPY | TCP_WRITE_FLAG_MORE);
"User: ",
168, TCP_WRITE_FLAG_COPY | TCP_WRITE_FLAG_MORE);
tcp_sent(pcb, NULL);
return ERR_OK;
}
@@ -200,4 +204,3 @@ void tcp_cmd_init(void) {
tcp_pcb = tcp_listen(tcp_pcb);
tcp_accept(tcp_pcb, tcp_cmd_accept);
}

View File

@@ -28,7 +28,10 @@ static void* tftp_open(const char* fname, const char* mode, uint8_t write);
static void tftp_close(void* handle);
static int tftp_read(void* handle, void* buf, int bytes);
static int tftp_write(void* handle, struct pbuf* p);
static struct tftp_context tftpContext_s = {.open = tftp_open, .close = tftp_close, .read = tftp_read, .write = tftp_write};
static struct tftp_context tftpContext_s = {.open = tftp_open,
.close = tftp_close,
.read = tftp_read,
.write = tftp_write};
/**
* @brief tftp custom file functions to set the offset and read the data
* @param[in,out] handle Custom file handles

View File

@@ -0,0 +1,301 @@
/**
* @file website_backend.h
* @brief Backend for the website to operate
* @author Toon B.
*/
// Includes
#include "website_backend.h"
// Static functions
static size_t wbe_get_images(char* images_string, size_t file_count_fs);
static inline void wbe_build_infostring(const llfs_file_t* file_list, char* info_string, const size_t cnt);
static void wbe_decoding_url(const char* encoded, char* decoded);
static uint32_t wbe_color_value(const char* rgb);
void wbe_init(void) {
httpd_init();
LOG_DEBUG("WBE", "Initialize webserver");
// Set starting LCD screen
wbe_display("Please Edit Me !", LCD_GREEN, LCD_BLACK, SD_IMG);
return;
}
int fs_open_custom(struct fs_file* file, const char* name) {
// Variables
llfs_file_t* wanted_file = llfs_file_open(name + 1);
size_t buffer_len = 0;
static char image_stringbuffer[STR_MAX] = "";
// The wanted file was found in the filesystem
if (wanted_file != NULL) {
// Debug info
LOG_DEBUG("WBE", "The file : %s was found", wanted_file->name);
// Give the information about the file to the client
file->data = (const char*)wanted_file->data;
file->len = (int)wanted_file->len;
file->index = (int)wanted_file->len;
file->is_custom_file = 1;
file->pextension = NULL;
file->flags = FS_FILE_FLAGS_HEADER_INCLUDED;
return 1;
}
/*
* The endpoint '/images.info' is called when the client wants to call for the
* information string that contains the names of the available images.
*/
if (strncmp(name, "/images.info", strlen("/images.info")) == 0) {
// Create the information string for the images in the filesystem
buffer_len = wbe_get_images(image_stringbuffer, llfs_file_count());
// Give the string back to the client
file->data = image_stringbuffer;
file->len = (int)buffer_len;
file->index = (int)buffer_len;
file->is_custom_file = 1;
file->pextension = NULL;
file->flags = FS_FILE_FLAGS_HEADER_INCLUDED;
return 1;
}
// Endpoint when client wants to send information to the backend
if (strncmp(name, "/cgi", 3) == 0) {
// Give the original index.html back to the client
wanted_file = llfs_file_open("index.html");
if (wanted_file != NULL) {
file->data = (const char*)wanted_file->data;
file->len = (int)wanted_file->len;
file->index = (int)wanted_file->len;
file->is_custom_file = 1;
file->pextension = NULL;
file->flags = FS_FILE_FLAGS_HEADER_INCLUDED;
return 1;
}
}
return 0;
}
void fs_close_custom(struct fs_file* file) {
}
/**
* @fn void wbe_get_images(char*, size_t)
* @brief Function searches filesystem for files with extention ".bmp" and ".gif" and put them
* combined in a string that needs to be send to the frontend.
*
* @param[in] images_string, string that will contain the image names
* @param[in] file_count_fs, the number of files in the filesystem
*/
static size_t wbe_get_images(char* images_string, size_t file_count_fs) {
// Allocate space for the files
llfs_file_t file_list[file_count_fs];
size_t file_count_bmp = 0;
size_t file_count_gif = 0;
// Reset string
strcpy(images_string, "");
// Get all the ".bmp" and ".gif" files
file_count_bmp = llfs_file_list(file_list, file_count_fs, ".bmp");
file_count_gif = llfs_file_list(file_list + file_count_bmp, file_count_fs, ".gif");
// Create the info string
wbe_build_infostring(file_list, images_string, file_count_gif + file_count_bmp);
// Debug info
LOG_DEBUG("WBE", "Images string : %s ", images_string);
// Return the length of the string
return strlen(images_string);
}
/**
* @fn void wbe_build_infostring(const llfs_file_t*, char*, const size_t)
* @brief Function adds the names of the files in "file_list" and adds them in "infoStr".
* The names are seperated by '|' characters.
*
* @param[in] file_list, list with files
* @param[in] info_string, string that will contain the filenames
* @param[in] cnt, the number of files
*/
static inline void wbe_build_infostring(const llfs_file_t* file_list, char* info_string, const size_t cnt) {
// Add the filenames to the info string
for (size_t i = 0; i < cnt; ++i) {
strncat(info_string, file_list[i].name, strlen(file_list[i].name));
strncat(info_string, "|", 2);
}
// Remove last seperator '|'
info_string[strlen(info_string) - 1] = '\0';
}
void httpd_cgi_handler(struct fs_file* file, const char* uri, int num_parm, char** pc_param, char** pc_value) {
// Variables
uint32_t vktxt = LCD_GREEN;
uint32_t vka = LCD_BLACK;
// Allocate space for parameters
char vtxt[STR_MAX]; // The sentence that we want to print on the LCD.
char vfo[STR_MAX]; // Name of the selected image on the website.
char s_vktxt[CLR_MAX]; // The color of the sentence. (in #RGB)
char s_vka[CLR_MAX]; // Color of the background. (also in #RGB)
if (strcmp("/cgi", uri) == 0) {
for (size_t i = 0; i < num_parm; ++i) {
// The given sentence
if (strcmp("vtxt", pc_param[i]) == 0) {
wbe_decoding_url(pc_value[i], vtxt);
}
// The color of the sentence
if (strcmp("vktxt", pc_param[i]) == 0) {
wbe_decoding_url(pc_value[i], s_vktxt);
// Convert to hex value
if (s_vktxt != NULL) {
vktxt = wbe_color_value(s_vktxt);
}
}
// Color of the background
if (strcmp("vka", pc_param[i]) == 0) {
wbe_decoding_url(pc_value[i], s_vka);
// convert to hex value
if (s_vka != NULL) {
vka = wbe_color_value(s_vka);
}
}
// Name of the image
if (strcmp("vfo", pc_param[i]) == 0) {
wbe_decoding_url(pc_value[i], vfo);
}
}
// Debug info
LOG_DEBUG("WBE", "CGI DATA : %s , %x , %x , %s", vtxt, vktxt, vka, vfo);
// Display on LCD
wbe_display(vtxt, vktxt, vka, vfo);
}
}
/**
* @fn void wbe_decoding_url(const char*, char*)
* @brief The given information from the CGI parameters are URL encoded.
* This function translates this encoded string in a readable
* decoded string. The lengthe of "char* decoded" is at least the
* length of "char* encoded" + 1.
*
* @param[in] encoded, the encoded URL string that needs to be decoded
* @param[in] decoded, string that will contain the decoded URL.
*/
static void wbe_decoding_url(const char* encoded, char* decoded) {
// Variables
char* endptr;
size_t decoded_index = 0;
size_t encoded_length = strlen(encoded);
uint32_t hex_val;
for (size_t i = 0; i < encoded_length; ++i) {
// If we encounter a %xx, decode it to ascii
if ((encoded[i] == '%') && (i + 2 < encoded_length)) {
// Decode %xx
hex_val = (uint32_t)strtoul(&encoded[i + 1], &endptr, 16);
// Check for conversion errors
if (endptr == &encoded[i + 1] || *endptr != '\0') {
LOG_DEBUG("WBE", "URL text conversion error");
}
decoded[decoded_index++] = (char)hex_val;
// Skip 2 characters
i += 2;
// If we encounter a +, add a space character
} else if (encoded[i] == '+') {
decoded[decoded_index++] = ' ';
// If no % or +, just put what stands in 'encoded'
} else {
decoded[decoded_index++] = encoded[i];
}
}
// Finish the string
decoded[decoded_index] = '\0';
}
static uint32_t wbe_color_value(const char* rgb) {
// Variables
char* endptr;
uint32_t color = LCD_BLACK;
char argb[11] = "0xff";
// Create argb string (also, skip the '#' char)
strncat(argb, rgb + 1, strlen(rgb + 1));
// Get argb value
color = (uint32_t)strtoul(argb, &endptr, 16);
// Check for conversion errors
if (*endptr != '\0') {
color = LCD_BLACK;
LOG_DEBUG("WBE", "URL color conversion error");
}
return color;
}
void wbe_display(const char* txt, const uint32_t txt_color, const uint32_t bg_color, const char* image) {
// Variables
lcd_gif_t* gif;
const char* extension;
// clear the screen
lcd_clear_text();
lcd_clear_images();
// Display the text and the background
lcd_set_bg_color_layer0(bg_color);
lcd_display_text(txt, 10, 10, txt_color, bg_color, LCD_FONT16);
// Get the extension of the file
extension = llfs_get_filename_ext(image);
// Check extension
if (extension != NULL) {
// Draw bmp
if (strcmp(extension, "bmp") == 0) {
lcd_draw_img_from_fs(image, IMG_X, IMG_Y);
// Draw gif
} else if (strcmp(extension, "gif") == 0) {
gif = lcd_draw_gif_from_fs(image, IMG_X, IMG_Y);
if (gif == NULL) {
LOG_DEBUG("WBE", "GIF could not be drawn");
}
// If nothing, then draw the standard image
} else {
lcd_draw_img_from_fs(SD_IMG, IMG_X, IMG_Y);
}
// If nothing, then draw the standard image
} else {
lcd_draw_img_from_fs(SD_IMG, IMG_X, IMG_Y);
}
}

View File

@@ -17,9 +17,10 @@ target_compile_definitions(tests
target_sources(tests
PRIVATE
${TEST_SOURCES}
../project/Core/Src/tftp.c
../project/Core/Src/llfs_data.c
../project/Core/Src/llfs.c
${CMAKE_SOURCE_DIR}
${CMAKE_SOURCE_DIR}/project/Core/Src/llfs_data.c
${CMAKE_SOURCE_DIR}/project/Core/Src/llfs.c
${CMAKE_SOURCE_DIR}/project/Core/Src/tftp.c
)
target_compile_options(tests PRIVATE $<$<CONFIG:Debug>:
@@ -35,7 +36,7 @@ target_include_directories(tests
PUBLIC
${CMAKE_CURRENT_LIST_DIR}
${PROJECT_BINARY_DIR}
../project/Core/Inc/
${CMAKE_SOURCE_DIR}/project/Core/Inc/
)
include(GoogleTest)

View File

@@ -12,7 +12,12 @@ int tftp_init(struct tftp_context* context) {
return 0;
}
void lcd_display_text(const char* text, uint16_t x_pos, uint16_t y_pos, uint32_t color, uint32_t bg_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) {
UNUSED(color);
UNUSED(bg_color);
UNUSED(font);

View File

@@ -4,8 +4,8 @@
extern "C" {
#endif
#include <stdlib.h>
#include <stdint.h>
#include <stdlib.h>
#ifndef UNUSED
#define UNUSED(x) (void)(x)
@@ -86,8 +86,7 @@ typedef void sFONT;
#define TCP_PRIO_MAX 127
#define IP_ADDR_ANY 0
typedef err_t (*tcp_recv_fn)(void *arg, struct tcp_pcb *tpcb,
struct pbuf *p, err_t err);
typedef err_t (*tcp_recv_fn)(void* arg, struct tcp_pcb* tpcb, struct pbuf* p, err_t err);
typedef err_t (*tcp_accept_fn)(void* arg, struct tcp_pcb* newpcb, err_t err);

View File

@@ -1,47 +1,35 @@
#include <gtest/gtest.h>
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "mocs.h"
#include "tftp.h"
tftp_custom_file_t file = {
.data = (char*)"1234567890",
.len = 11,
.name = (char*)"test.txt",
.offset = 0
};
tftp_custom_file_t write_file = {
.data = NULL,
.len = 0,
.name = (char*)"test.txt",
.offset = 0
};
tftp_custom_file_t file = {.data = (char*)"1234567890", .len = 11, .name = (char*)"test.txt", .offset = 0};
tftp_custom_file_t write_file = {.data = NULL, .len = 0, .name = (char*)"test.txt", .offset = 0};
TEST(TFTP, custom_fseek)
{
TEST(TFTP, custom_fseek) {
tftp_custom_fseek(&file, 5, SEEK_SET);
EXPECT_EQ(file.offset, 5);
tftp_custom_fseek(&file, 5, SEEK_CUR);
EXPECT_EQ(file.offset, 10);
}
TEST(TFTP, custom_fread)
{
TEST(TFTP, custom_fread) {
char buf[11];
tftp_custom_fseek(&file, 0, SEEK_SET);
size_t bytes = tftp_custom_fread(buf, 11, &file);
EXPECT_EQ(bytes, 10);
EXPECT_EQ(file.offset, 10);
EXPECT_EQ(bytes, 11);
EXPECT_EQ(file.offset, 11);
EXPECT_EQ(memcmp(buf, "1234567890", 10), 0);
memset(buf, 0, 11);
tftp_custom_fseek(&file, 0, SEEK_SET);
bytes = tftp_custom_fread(buf, 11, &file);
EXPECT_EQ(bytes, 10);
EXPECT_EQ(bytes, 11);
EXPECT_EQ(memcmp(buf, "1234567890", 10), 0);
memset(buf, 0, 11);

View File

@@ -4,8 +4,8 @@
extern "C" {
#endif
#include <stdlib.h>
#include <stdint.h>
#include <stdlib.h>
#include "mocs.h"
struct tftp_context {