mirror of
https://github.com/morcibacsi/PSAWifiDisplayControl.git
synced 2026-07-27 20:06:21 +00:00
Added files
This commit is contained in:
@@ -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
|
||||
@@ -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
@@ -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
|
||||
@@ -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
|
||||
@@ -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
@@ -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>
|
||||
@@ -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);
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 83 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.1 MiB |
@@ -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.
|
||||
|
||||
[](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
|
||||
|
||||

|
||||
|
||||
### 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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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">▲</button>
|
||||
</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<button type="button" class="btn btn-primary btn-lg btn-block btn-arrow" data-mode="97">◀</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">▶</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">▼</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"> Esc </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
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user