Added files

This commit is contained in:
peter.pinter
2019-12-22 20:46:58 +01:00
parent 727cb4a8ef
commit 3abf55111b
23 changed files with 1516 additions and 0 deletions
+12
View File
@@ -0,0 +1,12 @@
# EditorConfig is awesome: http://EditorConfig.org
# top-most EditorConfig file
root = true
# C++
[*.{c,cpp,h,ino}]
indent_style = space
tab_width = 4
end_of_line = crlf
charset = utf-8
+11
View File
@@ -0,0 +1,11 @@
*.h text diff=cpp eol=crlf
*.c text diff=cpp eol=crlf
*.cpp text diff=cpp eol=crlf
*.ino text diff=cpp eol=crlf
*.sln -text diff -merge eol=crlf
*.vcxproj -text diff -merge eol=crlf
*.vcxproj.filters -text diff -merge eol=crlf
.gitignore text
.gitattributes text
+220
View File
@@ -0,0 +1,220 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
build/
bld/
[Bb]in/
[Oo]bj/
# Visual Studio 2015 cache/options directory
.vs/
# Visual Micro sketch sub folder
__vm/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# DNX
project.lock.json
artifacts/
*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opensdf
*.sdf
*.cachefile
# Visual Studio profiler
*.psess
*.vsp
*.vspx
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding add-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# NCrunch
_NCrunch_*
.*crunch*.local.xml
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
## TODO: Comment the next line if you want to checkin your
## web deploy settings but do note that will include unencrypted
## passwords
#*.pubxml
*.publishproj
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/packages/*
# except build/, which is used as an MSBuild target.
!**/packages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/packages/repositories.config
# Windows Azure Build Output
csx/
*.build.csdef
# Windows Store app package directory
AppPackages/
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/
# Others
ClientBin/
[Ss]tyle[Cc]op.*
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.pfx
*.publishsettings
node_modules/
orleans.codegen.cs
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
# SQL Server files
*.mdf
*.ldf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
# Microsoft Fakes
FakesAssemblies/
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# LightSwitch generated files
GeneratedArtifacts/
_Pvt_Extensions/
ModelManifest.xml
version.txt
# elmah log files
error-*.xml
log.txt
+33
View File
@@ -0,0 +1,33 @@
#pragma once
#ifndef _Config_h
#define _Config_h
#define TASK_WATCHDOG_TIMEOUT 15
#define WIFI_AP_MODE
#ifdef WIFI_AP_MODE
#define WIFI_SSID "PSA display AP"
#define WIFI_PASSWORD "99999999"
#else
#define WIFI_SSID "Your_WiFI_SSID"
#define WIFI_PASSWORD "your_password"
#endif
#define SERIAL_BAUD 115200
// Test should be only used outside of the car
//#define TEST_MODE
#define HW_VERSION 11
#if HW_VERSION == 11
#define CAN_RX_PIN 33
#define CAN_TX_PIN 32
#elif HW_VERSION == 14
#define CAN_RX_PIN 18
#define CAN_TX_PIN 15
#endif
#endif
View File
+25
View File
@@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.29519.87
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PSAWifiDisplayControl", "PSAWifiDisplayControl.vcxproj", "{C5F80730-F44F-4478-BDAE-6634EFC2CA88}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x86 = Debug|x86
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{C5F80730-F44F-4478-BDAE-6634EFC2CA88}.Debug|x86.ActiveCfg = Debug|Win32
{C5F80730-F44F-4478-BDAE-6634EFC2CA88}.Debug|x86.Build.0 = Debug|Win32
{C5F80730-F44F-4478-BDAE-6634EFC2CA88}.Release|x86.ActiveCfg = Release|Win32
{C5F80730-F44F-4478-BDAE-6634EFC2CA88}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {E8C41217-983C-4B98-B6B0-6C68216F5638}
EndGlobalSection
EndGlobal
File diff suppressed because one or more lines are too long
+66
View File
@@ -0,0 +1,66 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<None Include="PSAWifiDisplayControl.ino" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="__vm\.PSAWifiDisplayControl.vsarduino.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\Can\AbstractCanMessageSender.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\Can\CanMessageSenderEsp32Arduino.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\Can\Structs\CanMenuStructs.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\Can\Structs\CanRadioRemoteStructs.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\Helpers\PacketGenerator.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\Helpers\Serializer.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\SerialPort\AbstractSerial.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\SerialPort\HardwareSerialAbs.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\Helpers\CaptiveRequestHandler.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Config.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\Helpers\index_html.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="PSAWifiDisplayControlMain.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\SerialPort\HardwareSerialAbs.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>
+204
View File
@@ -0,0 +1,204 @@
#pragma region Includes
#include <Arduino.h>
#include <esp_task_wdt.h>
#include <WiFi.h>
#include <DNSServer.h>
#include <ESPAsyncWebServer.h>
#include <ArduinoJson.h>
#include <AsyncJson.h>
#include "Config.h"
#include "src/SerialPort/AbstractSerial.h"
#include "src/SerialPort/HardwareSerialAbs.h"
#include "src/Can/CanMessageSenderEsp32Arduino.h"
#include "src/Can/Structs/CanMenuStructs.h"
#include "src/Can/Structs/CanRadioRemoteStructs.h"
#include "src/Helpers/CaptiveRequestHandler.h"
#include "src/Helpers/index_html.h"
#pragma endregion
TaskHandle_t CANIgnitionDataTask;
TaskHandle_t CANReadTask;
AbstractCanMessageSender* CANInterface;
AbsSer *serialPort;
CanRadioButtonPacketSender* radioButtonPacketSender;
DNSServer dnsServer;
AsyncWebServer* webServer;
IPAddress apIP(192, 168, 1, 1);
/// <summary>
/// Helper method to print an array to the serial port
/// </summary>
void PrintArrayToSerial(const uint8_t dataArray[], uint8_t dataArrayLength)
{
char tmp[3];
for (uint8_t i = 0; i < dataArrayLength; i++)
{
snprintf(tmp, 3, "%02X", dataArray[i]);
if (i != dataArrayLength - 1)
{
serialPort->print(tmp);
serialPort->print(" ");
}
else
{
serialPort->println(tmp);
}
}
}
#ifdef TEST_MODE
/// <summary>
/// This task is sending the ignition signal to the radio and the display so we can test without a BSI
/// </summary>
void CANIgnitionTaskFunction(void* parameter)
{
for (;;)
{
uint8_t data1[] = { 0x0E, 0x00, 0x05, 0x2F, 0x21, 0x80, 0x00, 0xA0 };
uint8_t data2[] = { 0x08, 0x32, 0x00, 0x1F, 0x00, 0x0D, 0x40, 0x01 };
CANInterface->SendMessage(0x036, 0, 8, data1);
CANInterface->SendMessage(0x0F6, 0, 8, data2);
vTaskDelay(65 / portTICK_PERIOD_MS);
esp_task_wdt_reset();
}
}
#endif
/// <summary>
/// This task is reading the CAN bus so when the SeekUp and SeekDown pressed simultaneously on the remote stalk we can change the mode on the display
/// </summary>
void CANReadTaskFunction(void * parameter)
{
uint8_t canReadMessage[20] = { 0 };
uint8_t canReadMessageLength = 0;
uint16_t canId = 0;
for (;;)
{
canId = 0;
canReadMessageLength = 0;
CANInterface->ReadMessage(&canId, &canReadMessageLength, canReadMessage);
if (canId > 0)
{
//serialPort->print(canId, HEX);
//serialPort->print(" ");
//PrintArrayToSerial(canReadMessage, canReadMessageLength);
if (canId == CAN_ID_RADIO_REMOTE)
{
const CanRadioRemotePacket packet = DeSerialize<CanRadioRemotePacket>(canReadMessage);
if (packet.data.RemoteButton.SeekDownPressed == 1 && packet.data.RemoteButton.Seek_UpPressed == 1)
{
radioButtonPacketSender->SendButtonCode(CONST_MODE_BUTTON);
}
}
}
vTaskDelay(10 / portTICK_PERIOD_MS);
esp_task_wdt_reset();
}
}
void notFound(AsyncWebServerRequest* request) {
serialPort->println("not found");
request->send(404, "text/plain", "Not found");
}
void setup()
{
serialPort = new HwSerAbs(Serial);
serialPort->begin(SERIAL_BAUD);
CANInterface = new CanMessageSenderEsp32Arduino(CAN_RX_PIN, CAN_TX_PIN);
CANInterface->Init();
radioButtonPacketSender = new CanRadioButtonPacketSender(CANInterface);
webServer = new AsyncWebServer(80);
IPAddress IP;
#ifdef WIFI_AP_MODE
WiFi.mode(WIFI_AP);
WiFi.softAP(WIFI_SSID, WIFI_PASSWORD);
delay(200);
WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0));
dnsServer.start(53, "*", apIP);
IP = WiFi.softAPIP();
#else
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
serialPort->println("Connecting to WiFi...");
}
IP = WiFi.localIP();
#endif
serialPort->print("IP address: ");
serialPort->println(IP);
AsyncCallbackJsonWebHandler* controlAcHandler = new AsyncCallbackJsonWebHandler("/rest/button", [](AsyncWebServerRequest* request, JsonVariant& json) {
int buttonId = json["button"];
serialPort->println(buttonId);
radioButtonPacketSender->SendButtonCode(buttonId);
AsyncResponseStream* response = request->beginResponseStream("application/json");
DynamicJsonDocument doc(1024);
doc["success"] = "OK";
serializeJson(doc, *response);
}, 1024);
webServer->addHandler(controlAcHandler);
webServer->onNotFound(notFound);
webServer->on("/index.html", HTTP_GET, [](AsyncWebServerRequest* request) {
request->send(200, "text/html", index_html);
});
webServer->addHandler(new CaptiveRequestHandler()).setFilter(ON_AP_FILTER);
webServer->begin();
xTaskCreatePinnedToCore(
CANReadTaskFunction, // Function to implement the task
"CANReadTask", // Name of the task
10000, // Stack size in words
NULL, // Task input parameter
0, // Priority of the task
&CANReadTask, // Task handle.
1); // Core where the task should run
#ifdef TEST_MODE
xTaskCreatePinnedToCore(
CANIgnitionTaskFunction, // Function to implement the task
"CANIgnitionTask", // Name of the task
10000, // Stack size in words
NULL, // Task input parameter
1, // Priority of the task
&CANIgnitionDataTask, // Task handle.
1); // Core where the task should run
#endif
esp_task_wdt_init(TASK_WATCHDOG_TIMEOUT, true);
}
void loop()
{
#ifdef WIFI_AP_MODE
dnsServer.processNextRequest();
#endif
vTaskDelay(10 / portTICK_PERIOD_MS);
}
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

+41
View File
@@ -0,0 +1,41 @@
# PSA WiFi display controller
### What is it ?
If you remove your factory fitted head-unit (RD4/RD43/RD45) from your car made by PSA Group you will loose the ability to set up the date and time on the multifunction display. This application creates a wifi access point where you can connect with your smart phone. It contains a captive portal so as soon as you are connected a message gets displayed on your phone to log in. If you tap the message a browser opens with the buttons which you can use to set up your display.
It also adds the ability to change the display mode by pressing the Seek Up and Seek Down buttons simultaneously on the radio remote stalk.
[![WATCH IT ON YOUTUBE](https://github.com/morcibacsi/PSAWifiDisplayControl/raw/master/images/youtube_preview.png)](https://www.youtube.com/watch?v=8nrd7SJgO-M "Watch it on YouTube")
The SSID of the access point is: **PSA display AP**
The password is: **99999999**
Of course you can modify this in the config.h file.
You can use this hardware to interface the CAN bus: [PSAVanCanBridgeHW][psavancanbridgehw]
### Schema
![schema](https://github.com/morcibacsi/PSAWifiDisplayControl/raw/master/images/schema.jpg)
### Building the project
To build the project you will need the following:
- Arduino IDE with ESP32 board support installed
- Libraries installed from the Used libraries section
### Used libraries
- [Arduino abstract serial][lib_abstract_serial] (tested version - commit: cda61dd)
- [Arduino Library for the ESP32 CAN Bus][lib_esp32_can]
- [ArduinoJson v6.13.0][lib_arduino_json]
- [AsyncTCP v1.1.1][lib_async_tcp]
- [ESPAsyncWebServer v1.2.3][lib_esp_async_webserver]
[lib_abstract_serial]: https://github.com/computergeek125/arduino-abstract-serial
[lib_esp32_can]: https://github.com/morcibacsi/ESP32-Arduino-CAN/tree/fix/can_cfg_to_constructor
[lib_arduino_json]: https://github.com/bblanchon/ArduinoJson.git
[lib_async_tcp]: https://github.com/me-no-dev/AsyncTCP
[lib_esp_async_webserver]: https://github.com/me-no-dev/ESPAsyncWebServer
[psavancanbridgehw]: https://github.com/morcibacsi/PSAVanCanBridgeHW
+13
View File
@@ -0,0 +1,13 @@
#pragma once
#ifndef _AbstractCanMessageSender_h
#define _AbstractCanMessageSender_h
class AbstractCanMessageSender {
public:
virtual void Init() = 0;
virtual void SendMessage(uint16_t canId, byte ext, byte sizeOfByteArray, unsigned char *byteArray) = 0;
virtual void ReadMessage(uint16_t* canId, uint8_t* len, uint8_t* buf) = 0;
};
#endif
+79
View File
@@ -0,0 +1,79 @@
#pragma once
#ifndef _CanMessageSenderEsp32Arduino_h
#define _CanMessageSenderEsp32Arduino_h
#include <ESP32CAN.h>
#include <CAN_config.h>
#include "AbstractCanMessageSender.h"
class CanMessageSenderEsp32Arduino : public AbstractCanMessageSender {
private:
const int rx_queue_size = 10;
CAN_device_t CAN_cfg;
SemaphoreHandle_t canSemaphore;
public:
CanMessageSenderEsp32Arduino(uint8_t rxPin, uint8_t txPin)
{
CAN_cfg.speed = CAN_SPEED_125KBPS;
CAN_cfg.tx_pin_id = (gpio_num_t)txPin;
CAN_cfg.rx_pin_id = (gpio_num_t)rxPin;
canSemaphore = xSemaphoreCreateMutex();
}
virtual void Init()
{
CAN_cfg.rx_queue = xQueueCreate(rx_queue_size, sizeof(CAN_frame_t));
ESP32Can.CANInit(CAN_cfg);
}
virtual void SendMessage(uint16_t canId, uint8_t ext, uint8_t sizeOfByteArray, unsigned char *byteArray)
{
CAN_frame_t tx_frame;
tx_frame.FIR.B.FF = CAN_frame_std;
tx_frame.MsgID = canId;
tx_frame.FIR.B.DLC = sizeOfByteArray;
for (size_t i = 0; i < sizeOfByteArray; i++)
{
tx_frame.data.u8[i] = byteArray[i];
}
xSemaphoreTake(canSemaphore, portMAX_DELAY);
ESP32Can.CANWriteFrame(&tx_frame);
xSemaphoreGive(canSemaphore);
}
virtual void ReadMessage(uint16_t *canId, uint8_t *len, uint8_t *buf)
{
CAN_frame_t rx_frame;
if (xQueueReceive(CAN_cfg.rx_queue, &rx_frame, 3 * portTICK_PERIOD_MS) == pdTRUE) {
if (rx_frame.FIR.B.FF == CAN_frame_std) {
//printf("New standard frame");
}
else {
//printf("New extended frame");
}
if (rx_frame.FIR.B.RTR == CAN_RTR) {
//printf(" RTR from 0x%08X, DLC %d\r\n", rx_frame.MsgID, rx_frame.FIR.B.DLC);
}
else {
*canId = rx_frame.MsgID;
*len = rx_frame.FIR.B.DLC;
//printf(" from 0x%08X, DLC %d, Data ", rx_frame.MsgID, rx_frame.FIR.B.DLC);
for (int i = 0; i < rx_frame.FIR.B.DLC; i++) {
buf[i] = rx_frame.data.u8[i];
//printf("0x%02X ", rx_frame.data.u8[i]);
}
//printf("\n");
}
}
}
};
#endif
+162
View File
@@ -0,0 +1,162 @@
// CanMenuStructs.h
#pragma once
#ifndef _CanMenuStructs_h
#define _CanMenuStructs_h
#include "../AbstractCanMessageSender.h"
#include "../../Helpers/PacketGenerator.h"
// CANID: 3E5
const uint16_t CAN_ID_MENU_BUTTONS = 0x3E5;
const int CONST_UP_ARROW = 119; //w
const int CONST_DOWN_ARROW = 115; //s
const int CONST_LEFT_ARROW = 97; //a
const int CONST_RIGHT_ARROW = 100; //d
const int CONST_ESC_BUTTON = 113; //q
const int CONST_OK_BUTTON = 101; //e
const int CONST_MENU_BUTTON = 109; //m
const int CONST_MODE_BUTTON = 110; //n
const int CONST_TRIP_BUTTON = 108; //n
/*
http://www.avrfreaks.net/comment/748331#comment-748331
0x40 = 0100 0000 - up
0x10 = 0001 0000 - down
0x04 = 0000 0100 - right
0x01 = 0000 0001 - left
*/
typedef struct {
uint8_t left : 1; // bit 0
uint8_t : 1; // bit 1
uint8_t right : 1; // bit 2
uint8_t : 1; // bit 3
uint8_t down : 1; // bit 4
uint8_t : 1; // bit 5
uint8_t up : 1; // bit 6
uint8_t : 1; // bit 7
} CanArrowsOnRadioStruct;
/*
0x40 = 0100 0000 - menu
*/
typedef struct {
uint8_t aircon : 1; // bit 0
uint8_t : 1; // bit 1
uint8_t : 1; // bit 2
uint8_t : 1; // bit 3
uint8_t phone : 1; // bit 4
uint8_t : 1; // bit 5
uint8_t menu : 1; // bit 6
uint8_t : 1; // bit 7
} CanMenuButtonStruct;
/*
0x40 = 0100 0000 - ok
0x10 = 0001 0000 - esc
*/
typedef struct {
uint8_t : 1; // bit 0
uint8_t : 1; // bit 1
uint8_t dark: 1; // bit 2
uint8_t : 1; // bit 3
uint8_t esc : 1; // bit 4
uint8_t : 1; // bit 5
uint8_t ok : 1; // bit 6
uint8_t : 1; // bit 7
} CanEscOkButtonStruct;
typedef struct {
uint8_t audio : 1; // bit 0
uint8_t : 1; // bit 1
uint8_t : 1; // bit 2
uint8_t : 1; // bit 3
uint8_t mode : 1; // bit 4
uint8_t : 1; // bit 5
uint8_t trip : 1; // bit 6
uint8_t : 1; // bit 7
} CanModeButtonStruct;
typedef struct CanMenuStruct {
CanMenuButtonStruct MenuField;
CanModeButtonStruct ModeField;
CanEscOkButtonStruct EscOkField;
uint8_t byte4;
uint8_t byte5;
CanArrowsOnRadioStruct ArrowsField;
};
typedef union CanMenuPacket {
CanMenuStruct data;
uint8_t CanMenuPacket[sizeof(CanMenuStruct)];
};
//https://stackoverflow.com/a/9196883/5453350
static int CONST_CAN_RADIO_MENUBUTTONS[] = { CONST_UP_ARROW, CONST_DOWN_ARROW, CONST_LEFT_ARROW, CONST_RIGHT_ARROW, CONST_ESC_BUTTON, CONST_OK_BUTTON, CONST_MENU_BUTTON, CONST_MODE_BUTTON, CONST_TRIP_BUTTON };
#pragma region Sender class
class CanRadioButtonPacketSender
{
AbstractCanMessageSender * canMessageSender;
public:
CanRadioButtonPacketSender(AbstractCanMessageSender * object)
{
canMessageSender = object;
}
void SendButtonCode(uint8_t buttonId)
{
PacketGenerator<CanMenuPacket> generator;
switch (buttonId) {
case CONST_MENU_BUTTON: {
generator.packet.data.MenuField.menu = 1;
break;
}
case CONST_OK_BUTTON: {
generator.packet.data.EscOkField.ok = 1;
break;
}
case CONST_ESC_BUTTON: {
generator.packet.data.EscOkField.esc = 1;
break;
}
case CONST_UP_ARROW: {
generator.packet.data.ArrowsField.up = 1;
break;
}
case CONST_DOWN_ARROW: {
generator.packet.data.ArrowsField.down = 1;
break;
}
case CONST_LEFT_ARROW: {
generator.packet.data.ArrowsField.left = 1;
break;
}
case CONST_RIGHT_ARROW: {
generator.packet.data.ArrowsField.right = 1;
break;
}
case CONST_MODE_BUTTON: {
generator.packet.data.ModeField.mode = 1;
break;
}
case CONST_TRIP_BUTTON: {
generator.packet.data.ModeField.trip = 1;
break;
}
default: {
break;
}
}
unsigned char *serializedPacket = generator.GetSerializedPacket();
canMessageSender->SendMessage(CAN_ID_MENU_BUTTONS, 0, 8, serializedPacket);
}
};
#pragma endregion
#endif
+75
View File
@@ -0,0 +1,75 @@
// CanRadioRemoteStructs.h
#pragma once
#ifndef _CanRadioRemoteStructs_h
#define _CanRadioRemoteStructs_h
#include "../AbstractCanMessageSender.h"
#include "../../Helpers/PacketGenerator.h"
// CANID: 21F
const uint16_t CAN_ID_RADIO_REMOTE = 0x21F;
// Read right to left in documentation
typedef struct {
uint8_t unknown0 : 1; // bit 0
uint8_t SourcePressed : 1; // bit 1
uint8_t VolumeDownPressed : 1; // bit 2
uint8_t VolumeUpPressed : 1; // bit 3
uint8_t unknown4 : 1; // bit 4
uint8_t unknown5 : 1; // bit 5
uint8_t SeekDownPressed : 1; // bit 6
uint8_t Seek_UpPressed : 1; // bit 7
} CanRadioRemoteButtonStruct;
// Read left to right in documentation
typedef struct CanRadioRemoteStruct {
CanRadioRemoteButtonStruct RemoteButton;
uint8_t ScrollPosition;
uint8_t Field2;
};
typedef union CanRadioRemotePacket {
CanRadioRemoteStruct data;
uint8_t CanRadioRemotePacket[sizeof(CanRadioRemoteStruct)];
};
#pragma region Sender class
class CanRadioRemoteButtonPacketSender
{
AbstractCanMessageSender * canMessageSender;
public:
CanRadioRemoteButtonPacketSender(AbstractCanMessageSender * object)
{
canMessageSender = object;
}
void Send(CanRadioRemoteButtonStruct button, uint8_t scrollPosition)
{
PacketGenerator<CanRadioRemotePacket> generator;
generator.packet.data.RemoteButton = button;
generator.packet.data.ScrollPosition = scrollPosition;
unsigned char *serializedPacket = generator.GetSerializedPacket();
canMessageSender->SendMessage(CAN_ID_RADIO_REMOTE, 0, sizeof(CanRadioRemotePacket), serializedPacket);
}
void SendAsByte(uint8_t button, uint8_t scrollPosition)
{
PacketGenerator<CanRadioRemotePacket> generator;
unsigned char *serializedPacket = generator.GetSerializedPacket();
serializedPacket[0] = button;
serializedPacket[1] = scrollPosition;
canMessageSender->SendMessage(CAN_ID_RADIO_REMOTE, 0, sizeof(CanRadioRemotePacket), serializedPacket);
}
};
#pragma endregion
#endif
+23
View File
@@ -0,0 +1,23 @@
//CaptiveRequestHandler.h
#pragma once
#ifndef _CaptiveRequestHandler_h
#define _CaptiveRequestHandler_h
#include <ESPAsyncWebServer.h>
class CaptiveRequestHandler : public AsyncWebHandler {
public:
CaptiveRequestHandler() {}
virtual ~CaptiveRequestHandler() {}
bool canHandle(AsyncWebServerRequest* request) {
//request->addInterestingHeader("ANY");
return true;
}
void handleRequest(AsyncWebServerRequest* request) {
request->redirect("/index.html");
}
};
#endif
+43
View File
@@ -0,0 +1,43 @@
// PacketGenerator.h
#pragma once
#ifndef _PacketGenerator_H
#define _PacketGenerator_H
#include "Serializer.h"
template <class T>
class PacketGenerator
{
public:
PacketGenerator();
~PacketGenerator();
T packet;
unsigned char * GetSerializedPacket();
private:
unsigned char * serializedPacket;
};
template <class T>
PacketGenerator<T>::PacketGenerator()
{
memset(&packet, 0, sizeof(packet));
}
template <class T>
PacketGenerator<T>::~PacketGenerator()
{
memset(&packet, 0, 0);
delete[] serializedPacket;
}
template <class T>
unsigned char * PacketGenerator<T>::GetSerializedPacket() {
serializedPacket = Serialize<T>(packet);
return serializedPacket;
}
#endif
+29
View File
@@ -0,0 +1,29 @@
// Serializer.h
#pragma once
#ifndef _Serializer_h
#define _Serializer_h
//http://forum.arduino.cc/index.php?topic=311337.0
//https://forum.arduino.cc/index.php?topic=406509.0
template <class T> T DeSerialize(const uint8_t b[])
{
//https://stackoverflow.com/a/13775983/5453350
//Re-make the struct
T tmp;
memcpy(&tmp, b, sizeof(tmp));
return tmp;
}
template <class T> unsigned char * Serialize(const T& packet)
{
//https://stackoverflow.com/a/14760796/5453350
unsigned char * b = new unsigned char[sizeof(packet)];
memcpy(b, &packet, sizeof(packet));//convert to byte array
return b;
}
#endif
+171
View File
@@ -0,0 +1,171 @@
#pragma once
#ifndef _index_html_h
#define _index_html_h
// This file contains the main html page
const char index_html[] PROGMEM = R"=====(
<!doctype html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>PSA WiFi display controller</title>
<style>
body {
background-color: white;
font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";
}
table {
border-collapse: collapse;
width: 100%;
}
th, td {
border: 0px solid black;
padding: 8px;
}
.aligncenter{
text-align: center;
}
.btn-primary:hover {
color: #fff;
background-color: #0069d9;
border-color: #0062cc;
}
.btn-block {
display: block;
width: 100%;
}
.btn-group-lg > .btn, .btn-lg {
padding: .5rem 1rem;
font-size: 1.25rem;
line-height: 1.5;
border-radius: .3rem;
}
.btn-arrow {
font-size: 28px !important;
}
.btn {
cursor: pointer;
display: inline-block;
font-weight: 400;
color: #212529;
text-align: center;
vertical-align: middle;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
background-color: transparent;
border: 1px solid transparent;
border-top-color: transparent;
border-right-color: transparent;
border-bottom-color: transparent;
border-left-color: transparent;
padding: .375rem .75rem;
font-size: 1rem;
line-height: 2.5;
border-radius: .25rem;
transition: color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;
}
.btn-primary {
color: #fff;
background-color: #007bff;
border-color: #007bff;
}
.h3, h3 {
font-size: 1.75rem;
text-align: center !important;
}
.h1, .h2, .h3, .h4, .h5, .h6, h1, h2, h3, h4, h5, h6 {
margin-bottom: .5rem;
font-weight: 500;
line-height: 1.2;
}
h1, h2, h3, h4, h5, h6 {
margin-top: 0;
margin-bottom: .5rem;
}
</style>
</head>
<body>
<div style="overflow-x:auto;">
<table id="table1">
<tr>
<td colspan="3"><h3>PSA WiFi display controller</h3></td>
</tr>
<tr>
<td></td>
<td class="aligncenter" >
<button type="button" class="btn btn-primary btn-lg btn-block btn-arrow" data-mode="119">&#9650;</button>
</td>
<td></td>
</tr>
<tr>
<td>
<button type="button" class="btn btn-primary btn-lg btn-block btn-arrow" data-mode="97">&#9664;</button>
</td>
<td class="aligncenter" >
<button type="button" class="btn btn-primary btn-lg btn-block btn-arrow" data-mode="101">OK</button>
</td>
<td>
<button type="button" class="btn btn-primary btn-lg btn-block btn-arrow" data-mode="100">&#9654;</button>
</td>
</tr>
<tr>
<td></td>
<td class="aligncenter">
<button type="button" class="btn btn-primary btn-lg btn-block btn-arrow" data-mode="115">&#9660;</button>
</td>
<td></td>
</tr>
<tr>
<td>
<button type="button" class="btn btn-primary btn-lg btn-block" data-mode="110">Mode</button>
</td>
<td>
<button type="button" class="btn btn-primary btn-lg btn-block" data-mode="109">Menu</button>
</td>
<td>
<button type="button" class="btn btn-primary btn-lg btn-block" data-mode="113">&nbsp;Esc&nbsp;&nbsp;</button>
</td>
</tr>
</table>
</div>
<!-- Optional JavaScript -->
<script type="text/javascript">
const buttons = document.getElementsByClassName("btn");
var i;
for (i = 0; i < buttons.length; i++) {
buttons[i].addEventListener("click", function(event) {
event.preventDefault();
var request = new XMLHttpRequest();
var url = "/rest/button";
request.open("POST", url, true);
request.setRequestHeader("Accept", "application/json, text/javascript, */*; q=0.01");
request.setRequestHeader("Content-Type", "application/json");
request.setRequestHeader("X-Requested-With", "XMLHttpRequest");
request.onreadystatechange = function () {
if (request.readyState === 4 && request.status === 200) {
var jsonData = JSON.parse(request.response);
console.log(jsonData);
}
};
var data = {"button": parseInt(this.dataset.mode)};
var jsonData = JSON.stringify(data);
request.send(jsonData);
});
}
</script>
</body>
</html>
)=====";
#endif
+56
View File
@@ -0,0 +1,56 @@
/*
* Written by Ryan Smith <computergeek125@gmail.com>
* Licensed under the MIT license. See LICENSE for details
*/
#ifndef AbstractSerial_H
#define AbstractSerial_H
#define LIBRARY_VERSION_ABSSER_H "0.1.0-alpha"
#include <inttypes.h>
#include "Stream.h"
#if defined(ARDUINO) && ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif
/**
* This library is intended to provide a Serial class that wraps around the
* various UART, USBSerial, and other interesting serial ports seen in the
* Arduino ecosystem. This is done by creating an API-compliant child class to
* this abstract class. This child class can be passed in to a function or
* library in abstract, so the program does not have to maintain its own wrapper
* code to make multiple types of serial port work.
*
* This base library will include code to abstract a few different serial ports
* and other stream-like devices that need to be accessed beyond their stream-
* like capabilities. Basically, this library's functions wrap the actual
* serial port's functions one-to-one.
*
* This class was based on the public interface of HardwareSerial. If there are
* any gaping incompatibilities with other kinds of ports, please open an issue
* on GitHub!
* <https://github.com/computergeek125/arduino-abstract-serial>
*/
class AbsSer : public Stream {
public:
virtual void begin(unsigned long baud, uint8_t config) =0;
virtual void begin(unsigned long baud) =0;
virtual void end() =0;
virtual int available(void) =0;
virtual int peek(void) =0;
virtual int read(void) =0;
virtual int availableForWrite(void) =0;
virtual void flush(void) =0;
using Print::write;
virtual size_t write(uint8_t n) =0;
virtual size_t write(unsigned long n) =0;
virtual size_t write(long n) =0;
virtual size_t write(unsigned int n) =0;
virtual size_t write(int n) =0;
virtual operator bool() =0;
};
#endif
+58
View File
@@ -0,0 +1,58 @@
/*
* Written by Ryan Smith <computergeek125@gmail.com>
* Licensed under the MIT license. See LICENSE for details
*/
#include "HardwareSerialAbs.h"
void HwSerAbs::begin(unsigned long baud, uint8_t config) {
port->begin(baud, config);
}
void HwSerAbs::begin(unsigned long baud) {
port->begin(baud);
}
void HwSerAbs::end() {
port->end();
}
int HwSerAbs::available(void) {
return port->available();
}
int HwSerAbs::peek(void) {
return port->peek();
}
int HwSerAbs::read(void) {
return port->read();
}
int HwSerAbs::availableForWrite(void) {
return port->availableForWrite();
}
void HwSerAbs::flush(void) {
port->flush();
}
size_t HwSerAbs::write(uint8_t n) {
return port->write(n);
}
size_t HwSerAbs::write(unsigned long n) {
return port->write(n);
}
size_t HwSerAbs::write(long n) {
return port->write(n);
}
size_t HwSerAbs::write(unsigned int n) {
return port->write(n);
}
size_t HwSerAbs::write(int n) {
return port->write(n);
}
+47
View File
@@ -0,0 +1,47 @@
/*
* Written by Ryan Smith <computergeek125@gmail.com>
* Licensed under the MIT license. See LICENSE for details
*/
#ifndef HardwareSerialAbs_H
#define HardwareSerialAbs_H
#define LIBRARY_VERSION_HWSERABS_H "0.1.0-alpha"
#ifdef ARDUINO_ARCH_SAMD
#warning "This library often does not compile properly on SAMD boards. Consider using UartSerialAbs instead."
#endif
#include <inttypes.h>
#include "AbstractSerial.h"
#if defined(ARDUINO) && ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif
class HwSerAbs : public AbsSer {
public:
inline HwSerAbs(HardwareSerial &hsport) {
port = &hsport;
}
void begin(unsigned long baud, uint8_t config);
void begin(unsigned long baud);
void end();
int available(void);
int peek(void);
int read(void);
int availableForWrite(void);
void flush(void);
using Print::write;
size_t write(uint8_t n);
size_t write(unsigned long n);
size_t write(long n);
size_t write(unsigned int n);
size_t write(int n);
inline operator bool() {return port;}
private:
HardwareSerial* port;
};
#endif