Files
OpenEPaperLink/ESP32_AP-Flasher/src/udp.cpp
Frank Kunz f311239c9c Add new AP hardware support OpenEPaperLink_ESP32-PoE-ISO_AP (#431)
* Fix filesystem mutex handling

This fixes the re-initialization of the filesystem mutex when the dyn storage
module is reinitialized. Using xSemaphoreCreateMutex would cause a memory leak
when the begin function is called multiple times and a semaphore leakage could
be caused by the re-initialization of the global fsMutex variable while the
semaphore is taken.

The fsMutex should not be taken while the logLine function is called as this
would cause a nested take of the fsMutex, which causes a deadlock.

Signed-off-by: Frank Kunz <mailinglists@kunz-im-inter.net>

* Fix hard coded littlefs in json upload

Signed-off-by: Frank Kunz <mailinglists@kunz-im-inter.net>

* Add new AP hardware support OpenEPaperLink_ESP32-PoE-ISO_AP

This is based on Olimex ESP32-PoE-ISO
https://www.olimex.com/Products/IoT/ESP32/ESP32-POE-ISO/open-source-hardware

It has a SD Card slot that is used to store all filesystem data on SD.
Use the prepare_sdcard.sh script to copy all needed data to an empty
SD card that is formatted with FAT filesystem. The AP firmware will format
the SD if an unformatted or from formatted card is used. This can be used
to intially prepare an empty SD card for usage.

For tag communication a ESP32-C6-WROOM-1(U) is used with the following
connection scheme:

ESP32-PoE-ISO  |  ESP32-C6-WROOM-1
---------------+------------------
  GPIO5        |    EN
  GPIO13       |    GPIO9
  GPIO36       |    GPIO3
  GPIO4        |    GPIO2
  GPIO33       |    GPIO24
  GPIO32       |    GPIO25
               |    GPIO8 pullup 5.1k

Signed-off-by: Frank Kunz <mailinglists@kunz-im-inter.net>

* Avoid error message log print when parsers.json is missing

Signed-off-by: Frank Kunz <mailinglists@kunz-im-inter.net>

* Workaround for IEEE802.15.4 modem stuck issue

The ESP32-C6 esp-idf based modem firmware can run into a case where it
does not receive data anymore from the tags. This can be detected when
it starts to print

"receive buffer full, drop the current frame"

and does not recover from that. In such a case a modem reset is triggered.

Signed-off-by: Frank Kunz <mailinglists@kunz-im-inter.net>

* Add OpenEPaperLink_ESP32-PoE-ISO_AP to release build

Signed-off-by: Frank Kunz <mailinglists@kunz-im-inter.net>

* Add OpenEPaperLink_ESP32-PoE-ISO_AP to condidional build

Signed-off-by: Frank Kunz <mailinglists@kunz-im-inter.net>

* Add Ethernet support

The ethernet support allows to make the network/internet connection
via LAN cable instead of WiFi. LAN is preferred, if a LAN cable is
connected and a valid IP configuration via DHCP can be obtained, WiFi
is switched off. If the LAN cable is disconnected, a fall back to
WiFi is done.

Use those defines in platform.ini for PHY settings:
ETHERNET_PHY_POWER: IO pin where the PHY can be switched of/on, can be
                    -1 if not used.
ETHERNET_CLK_MODE: PHY clock mode, see eth_clock_mode_t in ETH.h
ETHERNET_PHY_MDC: PHY MDC pin
ETHERNET_PHY_MDIO: PHY MDIO pin
ETHERNET_PHY_TYPE: PHY type, see eth_phy_type_t in ETH.h

Limitations:
- only DHCP is supported, no static IP configuration for LAN so far.
- If GPIO0 is used for one of the ETHERNET_CLK_MODE modes, then GPIO0
  cannot be used to clear the WiFi configuration.

Signed-off-by: Frank Kunz <mailinglists@kunz-im-inter.net>

---------

Signed-off-by: Frank Kunz <mailinglists@kunz-im-inter.net>
Co-authored-by: Frank Kunz <mailinglists@kunz-im-inter.net>
2025-03-27 00:48:05 +01:00

217 lines
6.7 KiB
C++

#include "udp.h"
#include <Arduino.h>
#include <WiFi.h>
#include "AsyncUDP.h"
#include "commstructs.h"
#include "newproto.h"
#include "serialap.h"
#include "tag_db.h"
#include "web.h"
#include "wifimanager.h"
#define UDPIP IPAddress(239, 10, 0, 1)
#define UDPPORT 16033
UDPcomm udpsync;
extern uint8_t channelList[6];
extern espSetChannelPower curChannel;
void init_udp() {
udpsync.init();
}
UDPcomm::UDPcomm() {
// Constructor
}
UDPcomm::~UDPcomm() {
// Destructor
}
void UDPcomm::init() {
if (config.discovery == 0) {
if (udp.listenMulticast(UDPIP, UDPPORT)) {
udp.onPacket([this](AsyncUDPPacket packet) {
if (packet.remoteIP() != wm.localIP()) {
this->processPacket(packet);
}
});
}
} else {
if (udp.listen(UDPPORT)) {
udp.onPacket([this](AsyncUDPPacket packet) {
if (packet.isBroadcast() && packet.remoteIP() != wm.localIP()) {
this->processPacket(packet);
}
});
}
}
setAPchannel();
}
void UDPcomm::processPacket(AsyncUDPPacket packet) {
if (config.runStatus == RUNSTATUS_STOP) {
return;
}
IPAddress senderIP = packet.remoteIP();
switch (packet.data()[0]) {
case PKT_AVAIL_DATA_INFO: {
espAvailDataReq adr;
memset(&adr, 0, sizeof(espAvailDataReq));
memcpy(&adr, &packet.data()[1], std::min(packet.length() - 1, sizeof(espAvailDataReq)));
processDataReq(&adr, false, senderIP);
break;
}
case PKT_XFER_COMPLETE: {
espXferComplete xfc;
memset(&xfc, 0, sizeof(espXferComplete));
memcpy(&xfc, &packet.data()[1], std::min(packet.length() - 1, sizeof(espXferComplete)));
processXferComplete(&xfc, false);
break;
}
case PKT_XFER_TIMEOUT: {
espXferComplete xfc;
memset(&xfc, 0, sizeof(espXferComplete));
memcpy(&xfc, &packet.data()[1], std::min(packet.length() - 1, sizeof(espXferComplete)));
processXferTimeout(&xfc, false);
break;
}
case PKT_AVAIL_DATA_REQ: {
pendingData pending;
memset(&pending, 0, sizeof(pendingData));
memcpy(&pending, &packet.data()[1], std::min(packet.length() - 1, sizeof(pendingData)));
prepareExternalDataAvail(&pending, senderIP);
break;
}
case PKT_APLIST_REQ: {
APlist APitem;
APitem.src = wm.localIP();
strcpy(APitem.alias, config.alias);
APitem.channelId = curChannel.channel;
APitem.tagCount = getTagCount();
APitem.version = apInfo.version;
uint8_t buffer[sizeof(struct APlist) + 1];
buffer[0] = PKT_APLIST_REPLY;
memcpy(buffer + 1, &APitem, sizeof(struct APlist));
writeUdpPacket(buffer, sizeof(buffer), senderIP);
break;
}
case PKT_APLIST_REPLY: {
APlist APreply;
memset(&APreply, 0, sizeof(APlist));
memcpy(&APreply, &packet.data()[1], std::min(packet.length() - 1, sizeof(APlist)));
// remove active channel from list
for (int i = 0; i < 6; i++) {
if (channelList[i] == APreply.channelId) channelList[i] = 0;
}
wsSendAPitem(&APreply);
break;
}
case PKT_TAGINFO: {
uint16_t syncversion = (packet.data()[2] << 8) | packet.data()[1];
if (syncversion != SYNC_VERSION) {
wsErr("Got a packet from " + senderIP.toString() + " with mismatched udp sync version. Update firmware!");
} else {
TagInfo* taginfoitem = (TagInfo*)&packet.data()[1];
updateTaginfoitem(taginfoitem, senderIP);
}
}
}
}
void autoselect(void* pvParameters) {
// reset channel list
uint8_t values[] = {11, 15, 20, 25, 26, 27};
memcpy(channelList, values, sizeof(values));
// wait 5s for channelList to collect all AP's
vTaskDelay(5000 / portTICK_PERIOD_MS);
curChannel.channel = 0;
for (int i = 0; i < 6; i++) {
if (channelList[i] > 0) {
curChannel.channel = channelList[i];
break;
}
}
if (curChannel.channel == 0) {
curChannel.channel = 11;
}
config.channel = curChannel.channel;
do {
vTaskDelay(1000 / portTICK_PERIOD_MS);
} while (!apInfo.isOnline);
sendChannelPower(&curChannel);
saveAPconfig();
vTaskDelay(1000 / portTICK_PERIOD_MS);
vTaskDelete(NULL);
}
void UDPcomm::getAPList() {
APlist APitem;
APitem.src = wm.localIP();
strcpy(APitem.alias, config.alias);
APitem.channelId = curChannel.channel;
APitem.tagCount = getTagCount();
APitem.version = apInfo.version;
wsSendAPitem(&APitem);
if (config.alias == 0) {
xTaskCreate(autoselect, "autoselect", 5000, NULL, 2, NULL);
}
uint8_t buffer[sizeof(struct APlist) + 1];
buffer[0] = PKT_APLIST_REQ;
memcpy(buffer + 1, &APitem, sizeof(struct APlist));
writeUdpPacket(buffer, sizeof(buffer), UDPIP);
}
void UDPcomm::netProcessDataReq(struct espAvailDataReq* eadr) {
uint8_t buffer[sizeof(struct espAvailDataReq) + 1];
buffer[0] = PKT_AVAIL_DATA_INFO;
memcpy(buffer + 1, eadr, sizeof(struct espAvailDataReq));
writeUdpPacket(buffer, sizeof(buffer), UDPIP);
}
void UDPcomm::netProcessXferComplete(struct espXferComplete* xfc) {
uint8_t buffer[sizeof(struct espXferComplete) + 1];
buffer[0] = PKT_XFER_COMPLETE;
memcpy(buffer + 1, xfc, sizeof(struct espXferComplete));
writeUdpPacket(buffer, sizeof(buffer), UDPIP);
}
void UDPcomm::netProcessXferTimeout(struct espXferComplete* xfc) {
uint8_t buffer[sizeof(struct espXferComplete) + 1];
buffer[0] = PKT_XFER_TIMEOUT;
memcpy(buffer + 1, xfc, sizeof(struct espXferComplete));
writeUdpPacket(buffer, sizeof(buffer), UDPIP);
}
void UDPcomm::netSendDataAvail(struct pendingData* pending) {
uint8_t buffer[sizeof(struct pendingData) + 1];
buffer[0] = PKT_AVAIL_DATA_REQ;
memcpy(buffer + 1, pending, sizeof(struct pendingData));
writeUdpPacket(buffer, sizeof(buffer), UDPIP);
}
void UDPcomm::netTaginfo(struct TagInfo* taginfoitem) {
uint8_t buffer[sizeof(struct TagInfo) + 1];
buffer[0] = PKT_TAGINFO;
memcpy(buffer + 1, taginfoitem, sizeof(struct TagInfo));
writeUdpPacket(buffer, sizeof(buffer), UDPIP);
}
void UDPcomm::writeUdpPacket(uint8_t *buffer, uint16_t len, IPAddress senderIP) {
if (config.discovery == 0) {
udp.writeTo(buffer, len, senderIP, UDPPORT);
} else {
udp.broadcastTo(buffer, len, UDPPORT);
}
}