├── hardware
└── Proto.pdf
├── software
├── pins.h
├── Q1.h
├── data
│ ├── mobile.css
│ ├── mobile.html
│ └── q1Funcs.js
├── spi.h
├── testpage.html
├── spi_register.h
├── spi.cpp
└── Q1.ino
├── notes.md
├── README.md
└── LICENSE
/hardware/Proto.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Extent421/Q1/HEAD/hardware/Proto.pdf
--------------------------------------------------------------------------------
/software/pins.h:
--------------------------------------------------------------------------------
1 | #define BLINK_PIN 2
2 | #define BEEP_PIN 5
3 |
4 | #define SETTINGS_RESET_PIN 4
5 |
--------------------------------------------------------------------------------
/notes.md:
--------------------------------------------------------------------------------
1 | rx5808 takes approx 26ms for rssi to stabilize after changing channels
2 |
3 | config layout:
4 | 10 bytes "Q1Settings"
5 | 1 byte settings version
6 | 1 byte enable connect to AP mode
7 | 2 byte frequency
8 | 2 byte low calibrate
9 | 2 byte med calibrate
10 | 2 byte high calibrate
11 | 32 character this station ssid
12 | 64 character this station password
13 | 32 character max ssid length
14 | 64 character max password length
15 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Q1 personal lap timer
2 |
3 | A transponderless lap timing system for 5.8ghz video systems
4 |
5 |
6 | required build environment
7 |
8 | * arduino 1.8.1
9 | * esp8266 community board 2.3.0
10 | * https://github.com/Links2004/arduinoWebSockets
11 | * https://github.com/me-no-dev/ESPAsyncTCP
12 |
13 | recomended
14 |
15 | * https://github.com/esp8266/arduino-esp8266fs-plugin
16 |
17 | rx5808 SPI mod
18 | * https://github.com/sheaivey/rx5808-pro-diversity/blob/master/docs/rx5808-spi-mod.md
19 |
--------------------------------------------------------------------------------
/software/Q1.h:
--------------------------------------------------------------------------------
1 | #define AP_CONNECT_DISABLED 0
2 | #define AP_AUTOCONNECT 1
3 |
4 | #define SETTINGS_VERSION 0
5 |
6 |
7 |
8 |
9 | #define PD_VCLAMP 1<<19
10 | #define PD_VAMP 1<<18
11 | #define PD_IF_DEMOD 1<<17
12 | #define PD_IFAF 1<<16
13 | #define PD_RSSI_SQUELCH 1<<15
14 | #define PD_REGBS 1<<14
15 | #define PD_REGIF 1<<13
16 | #define PD_BC 1<<12
17 | #define PD_DIV4 1<<11
18 | #define PD_5GVCO 1<<10
19 | #define PD_SYN 1<<9
20 | #define PD_AU6M 1<<8
21 | #define PD_6M 1<<7
22 | #define PD_AU6M5 1<<6
23 | #define PD_6M5 1<<5
24 | #define PD_REG1D8 1<<4
25 | #define PD_IFABF 1<<3
26 | #define PD_MIXER 1<<2
27 | #define PD_DIV80 1<<1
28 | #define PD_PLL1D8 1<<0
29 |
30 |
31 |
32 | typedef struct pulseData {
33 | unsigned long start;
34 | unsigned long end;
35 | };
36 |
37 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Mark Medrano
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/software/data/mobile.css:
--------------------------------------------------------------------------------
1 |
2 | body {
3 | font-size: 24px;
4 | }
5 |
6 | hr {
7 | -webkit-margin-before: 5px;
8 | -webkit-margin-after: 10px;
9 | }
10 | button {
11 | font-size: 20px;
12 | border-radius: 6px;
13 | box-shadow: 0 1px 3px 0 rgba(0,0,0,0.2);
14 | }
15 |
16 | textarea { display: block; }
17 |
18 | .tabrow {
19 | text-align: center;
20 | list-style: none;
21 | margin: 0;
22 | padding: 0;
23 | line-height: 1.3em;
24 | position: relative;
25 |
26 |
27 | }
28 | .tabrow:before {
29 | z-index: 1;
30 | }
31 | .tabrow:after {
32 | position: absolute;
33 | content: "";
34 | width: 100%;
35 | bottom: 0;
36 | left: 0;
37 | border-bottom: 1px solid #AAA;
38 | z-index: 1;
39 | }
40 | .tabrow li {
41 | margin: 0 -.25em;
42 | padding: 0 .9em;
43 | border: 1px solid #AAA;
44 | background: #ECECEC;
45 | display: inline-block;
46 | position: relative;
47 | z-index: 0;
48 | border-top-left-radius: 6px;
49 | border-top-right-radius: 6px;
50 | background: linear-gradient(to bottom, #ECECEC 50%, #D1D1D1 100%);
51 | text-shadow: 0 1px #FFF;
52 | bottom: 2px;
53 | }
54 | .tabrow li.selectedTab {
55 | background: #FFF;
56 | color: #000;
57 | z-index: 2;
58 | border-bottom-color: #FFF;
59 | }
60 | .tabrow li:before,
61 | .tabrow li:after {
62 | position: absolute;
63 | bottom: -1px;
64 | width: 6px;
65 | height: 6px;
66 | content: " ";
67 | border: 1px solid #AAA;
68 | }
69 | .tabrow li:before {
70 | left: -7px;
71 | border-bottom-right-radius: 4px;
72 | border-width: 0 1px 1px 0;
73 | }
74 | .tabrow li:after {
75 | right: -7px;
76 | border-bottom-left-radius: 4px;
77 | border-width: 0 0 1px 1px;
78 | }
79 | .tabrow li.selectedTab:before {
80 | box-shadow: 2px 2px 0 #FFF;
81 | }
82 | .tabrow li.selectedTab:after {
83 | box-shadow: -2px 2px 0 #FFF;
84 | }
85 |
86 |
87 |
88 | .leftTab {
89 | float: left;
90 | }
91 | .rightTab {
92 | float: right;
93 | }
94 | .hidden {
95 | display: none;
96 | }
97 | .tabPage {
98 | margin-top: 2px;
99 | }
100 |
101 | #mailResultsButton {
102 | float: right;
103 | }
104 | #settingsPage {
105 | top: 0px;
106 | z-index: 10;
107 | position: relative;
108 | width: 100%;
109 | }
110 | #settingsPage {
111 | top: 0px;
112 | z-index: 0;
113 | position: relative;
114 | width: 100%;
115 | }
116 |
117 | #lapContainer {
118 |
119 | }
120 |
121 | #lapScroll {
122 | height: 300px;
123 | overflow: auto;
124 | }
125 |
126 | #lapTimes {
127 | border-spacing: 0px;
128 | width:100%;
129 | }
130 |
131 | #lapTimes td {
132 | border-bottom: 1px solid #ddd;
133 | }
134 | #lapTimes td:first-child {
135 | width: 40px;
136 | }
137 | #lapTimes tr {
138 | background-color: #FFF;
139 | font-weight: bold;
140 | }
141 | #lapTimes .pace{
142 | background-color: #F80;
143 | color: #FFF;
144 | text-shadow:
145 | -1px -1px 0 #000,
146 | 1px -1px 0 #000,
147 | -1px 1px 0 #000,
148 | 1px 1px 0 #000;
149 | font-weight: bolder;
150 | }
151 | #lapTimes .best {
152 | background-color: #D50000;
153 | color: #FFF;
154 | text-shadow:
155 | -1px -1px 0 #000,
156 | 1px -1px 0 #000,
157 | -1px 1px 0 #000,
158 | 1px 1px 0 #000;
159 | font-weight: bolder;
160 | }
161 |
--------------------------------------------------------------------------------
/software/spi.h:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2015 David Ogilvy (MetalPhreak)
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | * SOFTWARE.
23 | */
24 |
25 | #ifndef SPI_APP_H
26 | #define SPI_APP_H
27 |
28 | #include "spi_register.h"
29 | #include "ets_sys.h"
30 | #include "osapi.h"
31 | //#include "uart.h"
32 | #include "os_type.h"
33 |
34 | //Define SPI hardware modules
35 | #define SPI 0
36 | #define HSPI 1
37 |
38 | #define SPI_CLK_USE_DIV 0
39 | #define SPI_CLK_80MHZ_NODIV 1
40 |
41 | #define SPI_BYTE_ORDER_HIGH_TO_LOW 1
42 | #define SPI_BYTE_ORDER_LOW_TO_HIGH 0
43 |
44 | #ifndef CPU_CLK_FREQ //Should already be defined in eagle_soc.h
45 | #define CPU_CLK_FREQ 80*1000000
46 | #endif
47 |
48 | //Define some default SPI clock settings
49 | #define SPI_CLK_PREDIV 10
50 | #define SPI_CLK_CNTDIV 4
51 | #define SPI_CLK_FREQ CPU_CLK_FREQ/(SPI_CLK_PREDIV*SPI_CLK_CNTDIV) // 80 / 20 = 4 MHz
52 |
53 |
54 |
55 | void spi_init(uint8 spi_no);
56 | void spi_mode(uint8 spi_no, uint8 spi_cpha,uint8 spi_cpol);
57 | void spi_init_gpio(uint8 spi_no, uint8 sysclk_as_spiclk);
58 | void spi_clock(uint8 spi_no, uint16 prediv, uint8 cntdiv);
59 | void spi_tx_byte_order(uint8 spi_no, uint8 byte_order);
60 | void spi_rx_byte_order(uint8 spi_no, uint8 byte_order);
61 | uint32 spi_transaction(uint8 spi_no, uint8 cmd_bits, uint16 cmd_data, uint32 addr_bits, uint32 addr_data, uint32 dout_bits, uint32 dout_data, uint32 din_bits, uint32 dummy_bits);
62 |
63 |
64 | //Expansion Macros
65 | #define spi_busy(spi_no) READ_PERI_REG(SPI_CMD(spi_no))&SPI_USR
66 |
67 | #define spi_txd(spi_no, bits, data) spi_transaction(spi_no, 0, 0, 0, 0, bits, (uint32) data, 0, 0)
68 | #define spi_tx8(spi_no, data) spi_transaction(spi_no, 0, 0, 0, 0, 8, (uint32) data, 0, 0)
69 | #define spi_tx16(spi_no, data) spi_transaction(spi_no, 0, 0, 0, 0, 16, (uint32) data, 0, 0)
70 | #define spi_tx32(spi_no, data) spi_transaction(spi_no, 0, 0, 0, 0, 32, (uint32) data, 0, 0)
71 |
72 | #define spi_rxd(spi_no, bits) spi_transaction(spi_no, 0, 0, 0, 0, 0, 0, bits, 0)
73 | #define spi_rx8(spi_no) spi_transaction(spi_no, 0, 0, 0, 0, 0, 0, 8, 0)
74 | #define spi_rx16(spi_no) spi_transaction(spi_no, 0, 0, 0, 0, 0, 0, 16, 0)
75 | #define spi_rx32(spi_no) spi_transaction(spi_no, 0, 0, 0, 0, 0, 0, 32, 0)
76 |
77 | #endif
78 |
--------------------------------------------------------------------------------
/software/data/mobile.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Personal Lap Timer
5 |
6 |
7 |
8 |
9 |
10 |
11 | - Main
12 |
13 | - Status
14 | - Settings
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | Status:
- Idle
23 |
24 |
25 |
26 |
27 |
28 |
29 |
35 |
36 |
37 |
38 |
39 |
40 |
41 | reading value: -
42 |
43 | Session: -
44 |
45 | WiFi Status: -
46 |
47 | WiFi IP: -
48 |
49 | Low calibrate value: -
50 |
51 | Threshold calibrate value: -
52 |
53 | High calibrate value: -
54 |
55 |
56 |
57 |
58 |
59 | channel number (in mhz):
60 |
62 |
63 | Minimum trigger length:
64 |
66 |
67 | Session number:
68 |
70 |
71 | Station SSID:
72 |
74 |
75 | Station Password:
76 |
78 |
79 | AP SSID:
80 |
82 |
83 | AP Password:
84 |
86 |
87 | Autoconnect to AP
88 |
89 |
90 | Email address:
91 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
--------------------------------------------------------------------------------
/software/testpage.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | WebSocket console
6 |
9 |
35 |
187 |
188 |
189 | Status: - Idle
190 | Buffer: - bytes
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 | reading value: -
225 | threshold value: -
226 |
227 |
228 |
229 |
230 |
231 | Log:
232 |
234 | |
235 |
236 | Time:
237 |
239 | |
240 |
241 |
242 |
243 |
244 |
--------------------------------------------------------------------------------
/software/data/q1Funcs.js:
--------------------------------------------------------------------------------
1 |
2 | var socket;
3 | var rawLog = "a";
4 | var overallBest = 999;
5 | var sessionBest = 999;
6 | var sessionNumber = 1;
7 | var doConnect = false;
8 |
9 | function logTime(msg) {
10 | rawLog = rawLog + msg + '\n';
11 |
12 | //var logger = document.getElementById('time');
13 | //logger.appendChild(document.createTextNode(msg + '\n'));
14 | //logger.scrollTop = logger.scrollHeight;
15 | var splitTime = msg.split(",");
16 | var overallFlag = false;
17 | var sessionFlag = false;
18 | thisTime = parseFloat(splitTime[2]);
19 | thisLap = parseInt(splitTime[0].match(/(\d+)$/)[0], 10);
20 |
21 | if(thisLap > 0) {
22 | if (thisTime < overallBest) {
23 | overallFlag = true;
24 | overallBest = thisTime;
25 | };
26 | if (thisTime < sessionBest) {
27 | sessionFlag = true;
28 | sessionBest = thisTime;
29 | };
30 | }
31 |
32 | var bestText = "";
33 | if (overallFlag) {
34 | bestText = ", overall best,";
35 | } else if (sessionFlag) {
36 | bestText = ", session best,";
37 | }
38 |
39 | var lapTable = document.getElementById('lapTimes');
40 | var newRow = lapTable.insertRow(-1);
41 | var lapCell = newRow.insertCell(0);
42 | var timeCell = newRow.insertCell(1);
43 | lapCell.innerHTML = thisLap;
44 | timeCell.innerHTML = thisTime;
45 | document.getElementById('lapScroll').scrollTop = document.getElementById('lapScroll').scrollHeight;
46 |
47 | refreshTimesTable();
48 |
49 | if ('speechSynthesis' in window) {
50 | // Synthesis support. Make your web apps talk!
51 | var readout = "lap "+ splitTime[0]+ bestText + ", "+ splitTime[2];
52 | var msg = new SpeechSynthesisUtterance(readout);
53 | msg.voice = speechSynthesis.getVoices().filter(function(voice) { return voice.name == localStorage.getItem("defaultVoice"); })[0];
54 | window.speechSynthesis.speak(msg);
55 | }
56 | }
57 | function status(msg) {
58 | document.getElementById('status').textContent = msg;
59 | }
60 |
61 | function connect() {
62 | doConnect = true;
63 | var host = location.hostname;
64 | if (host == "") { host = "10.0.2.128" };
65 |
66 | socket = new WebSocket('ws://'+host+':81/');
67 | status('Connecting');
68 | socket.onopen = function (event) {
69 | status('Connected');
70 | document.getElementById('connectButton').innerHTML = "Disconnect";
71 | document.getElementById('connectButton').onclick = disconnect;
72 | };
73 | socket.onmessage = function (event) {
74 | //log('RCVD: ' + event.data);
75 | if(event.data.startsWith('a') ){
76 | gotSample(event.data.substr(1, event.data.length));
77 | } else if(event.data.startsWith('c') ){
78 | logTime(event.data.substr(1, event.data.length));
79 | } else if(event.data.startsWith('d') ){
80 | gotSetting(event.data.substr(1, event.data.length));
81 | }
82 | };
83 | socket.onclose = function (event) {
84 | status('Disconnected.');
85 | document.getElementById('connectButton').innerHTML = "Connect";
86 | document.getElementById('connectButton').onclick = connect;
87 | if (doConnect) setTimeout(connect, 1000);
88 |
89 | };
90 | }
91 | function toggleSettings() {
92 | var div = document.getElementById("settingsPage");
93 | var display = div.style.display == "none" ? "block" : "none";
94 | div.style.display = display;
95 | if (display == "block") {
96 | getSettings();
97 | }
98 | div = document.getElementById("mainPage");
99 | display = display == "none" ? "block" : "none";
100 | div.style.display = display;
101 |
102 | }
103 |
104 | function openTab(evt, tabName) {
105 | var i;
106 | var pages = document.getElementsByClassName("tabPage");
107 | for (i = 0; i < pages.length; i++) {
108 | pages[i].style.display = "none";
109 | }
110 |
111 | var tabs = document.getElementsByClassName("tabLink");
112 | for (i = 0; i < tabs.length; i++) {
113 | tabs[i].className = tabs[i].className.replace("selectedTab", "");
114 | }
115 |
116 | evt.currentTarget.className += " selectedTab";
117 | document.getElementById(tabName).style.display = "block";
118 |
119 | if (tabName == "settingsPage") {
120 | getSettings();
121 | }
122 | if (tabName == "statusPage") {
123 | getStatus();
124 | start();
125 | } else {
126 | stop();
127 | }
128 |
129 |
130 | }
131 |
132 | function pageLoaded() {
133 | document.getElementById("emailAddress").value = localStorage.getItem("emailAddress");
134 |
135 |
136 | }
137 | function emailChange() {
138 | localStorage.setItem("emailAddress",document.getElementById("emailAddress").value);
139 | }
140 | function changeVoiceOption() {
141 | localStorage.setItem("defaultVoice",document.getElementById("voiceOption").value);
142 | }
143 | function disconnect() {
144 | doConnect = false;
145 | if (socket) {
146 | status('Disconnecting.');
147 | socket.close();
148 | }
149 | }
150 | function start() {
151 | if (socket) {
152 | socket.send('start');
153 | }
154 | }
155 | function stop() {
156 | if (socket) {
157 | socket.send('stop');
158 | }
159 | }
160 | function sendCalLow() {
161 | if (socket) {
162 | socket.send('callo');
163 | }
164 | }
165 | function sendCalThresh() {
166 | if (socket) {
167 | socket.send('calthresh');
168 | }
169 | }
170 | function sendCalHi() {
171 | if (socket) {
172 | socket.send('calhi');
173 | }
174 | }
175 | function sessionStart() {
176 | if (socket) {
177 | socket.send('sessionStart');
178 | var node = document.getElementById('lapTimes');
179 | while (node.firstChild) {
180 | node.removeChild(node.firstChild);
181 | }
182 |
183 | rawLog = "lap, timecode, laptime, trigger length, trigger offset\n";
184 | sessionBest = 999;
185 |
186 | window.onbeforeunload = function() {
187 | return "Reloading will erase current session.";
188 | };
189 |
190 | }
191 | }
192 | function sessionEnd() {
193 | if (socket) {
194 | socket.send('sessionEnd');
195 | }
196 | }
197 | function sendRestart() {
198 | if (socket) {
199 | socket.send('reboot');
200 | }
201 | }
202 | function getSettings() {
203 | if (socket) {
204 | socket.send('getSettings' );
205 | }
206 | }
207 | function getStatus() {
208 | if (socket) {
209 | socket.send('getStatus' );
210 | }
211 | }
212 | function setChannel() {
213 | if (socket) {
214 | socket.send('sch'+ document.getElementById("channelNumber").value );
215 | }
216 | }
217 | function minPulseChange() {
218 | if (socket) {
219 | socket.send('smp'+ document.getElementById("minPulse").value );
220 | }
221 | }
222 | function APSSIDChange() {
223 | if (socket) {
224 | socket.send('sapn'+ document.getElementById("APSSID").value );
225 | }
226 | }
227 | function APPasswordChange() {
228 | if (socket) {
229 | socket.send('sapp'+ document.getElementById("APPassword").value );
230 | }
231 | }
232 | function StationSSIDChange() {
233 | if (socket) {
234 | socket.send('sstn'+ document.getElementById("stationSSID").value );
235 | }
236 | }
237 | function StationPasswordChange() {
238 | if (socket) {
239 | socket.send('sstp'+ document.getElementById("stationPassword").value );
240 | }
241 | }
242 | function APModeChange() {
243 | if (socket) {
244 | if (document.getElementById('APMode').checked ) {
245 | socket.send('sapm1' );
246 | } else {
247 | socket.send('sapm0' );
248 | }
249 | }
250 | }
251 | function mailResult() {
252 | window.open('mailto:'+document.getElementById("emailAddress").value+'?subject=Times&body='+encodeURIComponent(rawLog));
253 | }
254 |
255 | function gotSample(data) {
256 | document.getElementById('readingValue').textContent = data;
257 | }
258 | function gotSetting(data) {
259 | var dSplit = data.split(":");
260 | if (dSplit.length != 2) return;
261 | switch(dSplit[0]) {
262 | case "freq":
263 | document.getElementById('channelNumber').value = dSplit[1];
264 | break;
265 | case "minPulse":
266 | document.getElementById('minPulse').value = dSplit[1];
267 | break;
268 | case "stationSSID":
269 | document.getElementById('stationSSID').value = dSplit[1];
270 | break;
271 | case "stationPass":
272 | document.getElementById('stationPassword').value = dSplit[1];
273 | break;
274 | case "APSSID":
275 | document.getElementById('APSSID').value = dSplit[1];
276 | break;
277 | case "APPass":
278 | document.getElementById('APPassword').value = dSplit[1];
279 | break;
280 | case "APMode":
281 | if ( dSplit[1] == "1" ) {
282 | document.getElementById('APMode').checked = true;
283 |
284 | } else {
285 | document.getElementById('APMode').checked = false;
286 | }
287 | break;
288 | case "APstatus":
289 | document.getElementById('wifiStatusValue').textContent = dSplit[1];
290 | break;
291 | case "APIP":
292 | document.getElementById('wifiIPValue').textContent = dSplit[1];
293 | break;
294 | case "minCal":
295 | document.getElementById('loCalibrateValue').textContent = dSplit[1];
296 | break;
297 | case "maxCal":
298 | document.getElementById('hiCalibrateValue').textContent = dSplit[1];
299 | break;
300 | case "threshCal":
301 | document.getElementById('midCalibrateValue').textContent = dSplit[1];
302 | break;
303 | case "sessionRunning":
304 | console.log(data);
305 | if ( dSplit[1] == "1" ) {
306 | document.getElementById('sessionValue').textContent = "Running";
307 | } else {
308 | document.getElementById('sessionValue').textContent = "Stopped";
309 | }
310 |
311 | break;
312 |
313 |
314 |
315 | default:
316 | break;
317 |
318 | }
319 |
320 | //document.getElementById('APSSID').value = dSplit[1];
321 | }
322 | function update() {
323 | if (socket) {
324 | document.getElementById('readyState').textContent = socket.readyState;
325 | } else {
326 | document.getElementById('readyState').textContent = '-';
327 | }
328 | }
329 | function refreshTimesTable() {
330 | var table = document.getElementById('lapTimes');
331 | for (var i = 0, row; row = table.rows[i]; i++) {
332 | //iterate through rows
333 | //rows would be accessed using the "row" variable assigned in the for loop
334 | var timeValue = parseInt(row.cells[1].innerHTML) ;
335 | if (parseInt(sessionBest) == timeValue){
336 | row.className = "best";
337 | } else if (parseInt(sessionBest)+1 == timeValue) {
338 | row.className = "pace";
339 | } else {
340 | row.className = "";
341 | }
342 |
343 | }
344 | }
345 |
346 |
347 | window.speechSynthesis.onvoiceschanged = function() {
348 | var voices = speechSynthesis.getVoices();
349 | voices.forEach(function (voice, i) {
350 | var option = document.createElement('option');
351 | option.value = voice.name;
352 | option.innerHTML = voice.name;
353 | document.getElementById("voiceOption").appendChild(option);
354 | });
355 | document.getElementById("voiceOption").value = localStorage.getItem("defaultVoice");
356 | };
357 | setInterval(update, 10);
--------------------------------------------------------------------------------
/software/spi_register.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2010 - 2011 Espressif System
3 | * Modified by David Ogilvy (MetalPhreak)
4 | * Based on original file included in SDK 1.0.0
5 | *
6 | * Missing defines from previous SDK versions have
7 | * been added and are noted with comments. The
8 | * names of these defines are likely to change.
9 | */
10 |
11 | #ifndef SPI_REGISTER_H_INCLUDED
12 | #define SPI_REGISTER_H_INCLUDED
13 |
14 | #define REG_SPI_BASE(i) (0x60000200-i*0x100)
15 |
16 | #define SPI_CMD(i) (REG_SPI_BASE(i) + 0x0)
17 | #define SPI_FLASH_READ (BIT(31)) //From previous SDK
18 | #define SPI_FLASH_WREN (BIT(30)) //From previous SDK
19 | #define SPI_FLASH_WRDI (BIT(29)) //From previous SDK
20 | #define SPI_FLASH_RDID (BIT(28)) //From previous SDK
21 | #define SPI_FLASH_RDSR (BIT(27)) //From previous SDK
22 | #define SPI_FLASH_WRSR (BIT(26)) //From previous SDK
23 | #define SPI_FLASH_PP (BIT(25)) //From previous SDK
24 | #define SPI_FLASH_SE (BIT(24)) //From previous SDK
25 | #define SPI_FLASH_BE (BIT(23)) //From previous SDK
26 | #define SPI_FLASH_CE (BIT(22)) //From previous SDK
27 | #define SPI_FLASH_DP (BIT(21)) //From previous SDK
28 | #define SPI_FLASH_RES (BIT(20)) //From previous SDK
29 | #define SPI_FLASH_HPM (BIT(19)) //From previous SDK
30 | #define SPI_USR (BIT(18))
31 |
32 | #define SPI_ADDR(i) (REG_SPI_BASE(i) + 0x4)
33 |
34 | #define SPI_CTRL(i) (REG_SPI_BASE(i) + 0x8)
35 | #define SPI_WR_BIT_ORDER (BIT(26))
36 | #define SPI_RD_BIT_ORDER (BIT(25))
37 | #define SPI_QIO_MODE (BIT(24))
38 | #define SPI_DIO_MODE (BIT(23))
39 | #define SPI_TWO_BYTE_STATUS_EN (BIT(22)) //From previous SDK
40 | #define SPI_WP_REG (BIT(21)) //From previous SDK
41 | #define SPI_QOUT_MODE (BIT(20))
42 | #define SPI_SHARE_BUS (BIT(19)) //From previous SDK
43 | #define SPI_HOLD_MODE (BIT(18)) //From previous SDK
44 | #define SPI_ENABLE_AHB (BIT(17)) //From previous SDK
45 | #define SPI_SST_AAI (BIT(16)) //From previous SDK
46 | #define SPI_RESANDRES (BIT(15)) //From previous SDK
47 | #define SPI_DOUT_MODE (BIT(14))
48 | #define SPI_FASTRD_MODE (BIT(13))
49 |
50 | #define SPI_CTRL1(i) (REG_SPI_BASE (i) + 0xC) //From previous SDK. Removed _FLASH_ from name to match other registers.
51 | #define SPI_CS_HOLD_DELAY 0x0000000F //Espressif BBS
52 | #define SPI_CS_HOLD_DELAY_S 28 //Espressif BBS
53 | #define SPI_CS_HOLD_DELAY_RES 0x00000FFF //Espressif BBS
54 | #define SPI_CS_HOLD_DELAY_RES_S 16 //Espressif BBS
55 | #define SPI_BUS_TIMER_LIMIT 0x0000FFFF //From previous SDK
56 | #define SPI_BUS_TIMER_LIMIT_S 0 //From previous SDK
57 |
58 |
59 | #define SPI_RD_STATUS(i) (REG_SPI_BASE(i) + 0x10)
60 | #define SPI_STATUS_EXT 0x000000FF //From previous SDK
61 | #define SPI_STATUS_EXT_S 24 //From previous SDK
62 | #define SPI_WB_MODE 0x000000FF //From previous SDK
63 | #define SPI_WB_MODE_S 16 //From previous SDK
64 | #define SPI_FLASH_STATUS_PRO_FLAG (BIT(7)) //From previous SDK
65 | #define SPI_FLASH_TOP_BOT_PRO_FLAG (BIT(5)) //From previous SDK
66 | #define SPI_FLASH_BP2 (BIT(4)) //From previous SDK
67 | #define SPI_FLASH_BP1 (BIT(3)) //From previous SDK
68 | #define SPI_FLASH_BP0 (BIT(2)) //From previous SDK
69 | #define SPI_FLASH_WRENABLE_FLAG (BIT(1)) //From previous SDK
70 | #define SPI_FLASH_BUSY_FLAG (BIT(0)) //From previous SDK
71 |
72 | #define SPI_CTRL2(i) (REG_SPI_BASE(i) + 0x14)
73 | #define SPI_CS_DELAY_NUM 0x0000000F
74 | #define SPI_CS_DELAY_NUM_S 28
75 | #define SPI_CS_DELAY_MODE 0x00000003
76 | #define SPI_CS_DELAY_MODE_S 26
77 | #define SPI_MOSI_DELAY_NUM 0x00000007
78 | #define SPI_MOSI_DELAY_NUM_S 23
79 | #define SPI_MOSI_DELAY_MODE 0x00000003 //mode 0 : posedge; data set at positive edge of clk
80 | //mode 1 : negedge + 1 cycle delay, only if freq<10MHz ; data set at negitive edge of clk
81 | //mode 2 : Do not use this mode.
82 | #define SPI_MOSI_DELAY_MODE_S 21
83 | #define SPI_MISO_DELAY_NUM 0x00000007
84 | #define SPI_MISO_DELAY_NUM_S 18
85 | #define SPI_MISO_DELAY_MODE 0x00000003
86 | #define SPI_MISO_DELAY_MODE_S 16
87 | #define SPI_CK_OUT_HIGH_MODE 0x0000000F
88 | #define SPI_CK_OUT_HIGH_MODE_S 12
89 | #define SPI_CK_OUT_LOW_MODE 0x0000000F
90 | #define SPI_CK_OUT_LOW_MODE_S 8
91 | #define SPI_HOLD_TIME 0x0000000F
92 | #define SPI_HOLD_TIME_S 4
93 | #define SPI_SETUP_TIME 0x0000000F
94 | #define SPI_SETUP_TIME_S 0
95 |
96 | #define SPI_CLOCK(i) (REG_SPI_BASE(i) + 0x18)
97 | #define SPI_CLK_EQU_SYSCLK (BIT(31))
98 | #define SPI_CLKDIV_PRE 0x00001FFF
99 | #define SPI_CLKDIV_PRE_S 18
100 | #define SPI_CLKCNT_N 0x0000003F
101 | #define SPI_CLKCNT_N_S 12
102 | #define SPI_CLKCNT_H 0x0000003F
103 | #define SPI_CLKCNT_H_S 6
104 | #define SPI_CLKCNT_L 0x0000003F
105 | #define SPI_CLKCNT_L_S 0
106 |
107 | #define SPI_USER(i) (REG_SPI_BASE(i) + 0x1C)
108 | #define SPI_USR_COMMAND (BIT(31))
109 | #define SPI_USR_ADDR (BIT(30))
110 | #define SPI_USR_DUMMY (BIT(29))
111 | #define SPI_USR_MISO (BIT(28))
112 | #define SPI_USR_MOSI (BIT(27))
113 | #define SPI_USR_DUMMY_IDLE (BIT(26)) //From previous SDK
114 | #define SPI_USR_MOSI_HIGHPART (BIT(25))
115 | #define SPI_USR_MISO_HIGHPART (BIT(24))
116 | #define SPI_USR_PREP_HOLD (BIT(23)) //From previous SDK
117 | #define SPI_USR_CMD_HOLD (BIT(22)) //From previous SDK
118 | #define SPI_USR_ADDR_HOLD (BIT(21)) //From previous SDK
119 | #define SPI_USR_DUMMY_HOLD (BIT(20)) //From previous SDK
120 | #define SPI_USR_DIN_HOLD (BIT(19)) //From previous SDK
121 | #define SPI_USR_DOUT_HOLD (BIT(18)) //From previous SDK
122 | #define SPI_USR_HOLD_POL (BIT(17)) //From previous SDK
123 | #define SPI_SIO (BIT(16))
124 | #define SPI_FWRITE_QIO (BIT(15))
125 | #define SPI_FWRITE_DIO (BIT(14))
126 | #define SPI_FWRITE_QUAD (BIT(13))
127 | #define SPI_FWRITE_DUAL (BIT(12))
128 | #define SPI_WR_BYTE_ORDER (BIT(11))
129 | #define SPI_RD_BYTE_ORDER (BIT(10))
130 | #define SPI_AHB_ENDIAN_MODE 0x00000003 //From previous SDK
131 | #define SPI_AHB_ENDIAN_MODE_S 8 //From previous SDK
132 | #define SPI_CK_OUT_EDGE (BIT(7))
133 | #define SPI_CK_I_EDGE (BIT(6))
134 | #define SPI_CS_SETUP (BIT(5))
135 | #define SPI_CS_HOLD (BIT(4))
136 | #define SPI_AHB_USR_COMMAND (BIT(3)) //From previous SDK
137 | #define SPI_FLASH_MODE (BIT(2))
138 | #define SPI_AHB_USR_COMMAND_4BYTE (BIT(1)) //From previous SDK
139 | #define SPI_DOUTDIN (BIT(0)) //From previous SDK
140 |
141 | //AHB = http://en.wikipedia.org/wiki/Advanced_Microcontroller_Bus_Architecture ?
142 |
143 |
144 | #define SPI_USER1(i) (REG_SPI_BASE(i) + 0x20)
145 | #define SPI_USR_ADDR_BITLEN 0x0000003F
146 | #define SPI_USR_ADDR_BITLEN_S 26
147 | #define SPI_USR_MOSI_BITLEN 0x000001FF
148 | #define SPI_USR_MOSI_BITLEN_S 17
149 | #define SPI_USR_MISO_BITLEN 0x000001FF
150 | #define SPI_USR_MISO_BITLEN_S 8
151 | #define SPI_USR_DUMMY_CYCLELEN 0x000000FF
152 | #define SPI_USR_DUMMY_CYCLELEN_S 0
153 |
154 | #define SPI_USER2(i) (REG_SPI_BASE(i) + 0x24)
155 | #define SPI_USR_COMMAND_BITLEN 0x0000000F
156 | #define SPI_USR_COMMAND_BITLEN_S 28
157 | #define SPI_USR_COMMAND_VALUE 0x0000FFFF
158 | #define SPI_USR_COMMAND_VALUE_S 0
159 |
160 | #define SPI_WR_STATUS(i) (REG_SPI_BASE(i) + 0x28)
161 | //previously defined as SPI_FLASH_USER3. No further info available.
162 |
163 | #define SPI_PIN(i) (REG_SPI_BASE(i) + 0x2C)
164 | #define SPI_IDLE_EDGE (BIT(29))
165 | #define SPI_CS2_DIS (BIT(2))
166 | #define SPI_CS1_DIS (BIT(1))
167 | #define SPI_CS0_DIS (BIT(0))
168 |
169 | #define SPI_SLAVE(i) (REG_SPI_BASE(i) + 0x30)
170 | #define SPI_SYNC_RESET (BIT(31))
171 | #define SPI_SLAVE_MODE (BIT(30))
172 | #define SPI_SLV_WR_RD_BUF_EN (BIT(29))
173 | #define SPI_SLV_WR_RD_STA_EN (BIT(28))
174 | #define SPI_SLV_CMD_DEFINE (BIT(27))
175 | #define SPI_TRANS_CNT 0x0000000F
176 | #define SPI_TRANS_CNT_S 23
177 | #define SPI_SLV_LAST_STATE 0x00000007 //From previous SDK
178 | #define SPI_SLV_LAST_STATE_S 20 //From previous SDK
179 | #define SPI_SLV_LAST_COMMAND 0x00000007 //From previous SDK
180 | #define SPI_SLV_LAST_COMMAND_S 17 //From previous SDK
181 | #define SPI_CS_I_MODE 0x00000003 //From previous SDK
182 | #define SPI_CS_I_MODE_S 10 //From previous SDK
183 | #define SPI_TRANS_DONE_EN (BIT(9))
184 | #define SPI_SLV_WR_STA_DONE_EN (BIT(8))
185 | #define SPI_SLV_RD_STA_DONE_EN (BIT(7))
186 | #define SPI_SLV_WR_BUF_DONE_EN (BIT(6))
187 | #define SPI_SLV_RD_BUF_DONE_EN (BIT(5))
188 | #define SLV_SPI_INT_EN 0x0000001f
189 | #define SLV_SPI_INT_EN_S 5
190 | #define SPI_TRANS_DONE (BIT(4))
191 | #define SPI_SLV_WR_STA_DONE (BIT(3))
192 | #define SPI_SLV_RD_STA_DONE (BIT(2))
193 | #define SPI_SLV_WR_BUF_DONE (BIT(1))
194 | #define SPI_SLV_RD_BUF_DONE (BIT(0))
195 |
196 | #define SPI_SLAVE1(i) (REG_SPI_BASE(i) + 0x34)
197 | #define SPI_SLV_STATUS_BITLEN 0x0000001F
198 | #define SPI_SLV_STATUS_BITLEN_S 27
199 | #define SPI_SLV_STATUS_FAST_EN (BIT(26)) //From previous SDK
200 | #define SPI_SLV_STATUS_READBACK (BIT(25)) //From previous SDK
201 | #define SPI_SLV_BUF_BITLEN 0x000001FF
202 | #define SPI_SLV_BUF_BITLEN_S 16
203 | #define SPI_SLV_RD_ADDR_BITLEN 0x0000003F
204 | #define SPI_SLV_RD_ADDR_BITLEN_S 10
205 | #define SPI_SLV_WR_ADDR_BITLEN 0x0000003F
206 | #define SPI_SLV_WR_ADDR_BITLEN_S 4
207 | #define SPI_SLV_WRSTA_DUMMY_EN (BIT(3))
208 | #define SPI_SLV_RDSTA_DUMMY_EN (BIT(2))
209 | #define SPI_SLV_WRBUF_DUMMY_EN (BIT(1))
210 | #define SPI_SLV_RDBUF_DUMMY_EN (BIT(0))
211 |
212 |
213 |
214 | #define SPI_SLAVE2(i) (REG_SPI_BASE(i) + 0x38)
215 | #define SPI_SLV_WRBUF_DUMMY_CYCLELEN 0X000000FF
216 | #define SPI_SLV_WRBUF_DUMMY_CYCLELEN_S 24
217 | #define SPI_SLV_RDBUF_DUMMY_CYCLELEN 0X000000FF
218 | #define SPI_SLV_RDBUF_DUMMY_CYCLELEN_S 16
219 | #define SPI_SLV_WRSTR_DUMMY_CYCLELEN 0X000000FF
220 | #define SPI_SLV_WRSTR_DUMMY_CYCLELEN_S 8
221 | #define SPI_SLV_RDSTR_DUMMY_CYCLELEN 0x000000FF
222 | #define SPI_SLV_RDSTR_DUMMY_CYCLELEN_S 0
223 |
224 | #define SPI_SLAVE3(i) (REG_SPI_BASE(i) + 0x3C)
225 | #define SPI_SLV_WRSTA_CMD_VALUE 0x000000FF
226 | #define SPI_SLV_WRSTA_CMD_VALUE_S 24
227 | #define SPI_SLV_RDSTA_CMD_VALUE 0x000000FF
228 | #define SPI_SLV_RDSTA_CMD_VALUE_S 16
229 | #define SPI_SLV_WRBUF_CMD_VALUE 0x000000FF
230 | #define SPI_SLV_WRBUF_CMD_VALUE_S 8
231 | #define SPI_SLV_RDBUF_CMD_VALUE 0x000000FF
232 | #define SPI_SLV_RDBUF_CMD_VALUE_S 0
233 |
234 | //Previous SDKs referred to these following registers as SPI_C0 etc.
235 |
236 | #define SPI_W0(i) (REG_SPI_BASE(i) +0x40)
237 | #define SPI_W1(i) (REG_SPI_BASE(i) +0x44)
238 | #define SPI_W2(i) (REG_SPI_BASE(i) +0x48)
239 | #define SPI_W3(i) (REG_SPI_BASE(i) +0x4C)
240 | #define SPI_W4(i) (REG_SPI_BASE(i) +0x50)
241 | #define SPI_W5(i) (REG_SPI_BASE(i) +0x54)
242 | #define SPI_W6(i) (REG_SPI_BASE(i) +0x58)
243 | #define SPI_W7(i) (REG_SPI_BASE(i) +0x5C)
244 | #define SPI_W8(i) (REG_SPI_BASE(i) +0x60)
245 | #define SPI_W9(i) (REG_SPI_BASE(i) +0x64)
246 | #define SPI_W10(i) (REG_SPI_BASE(i) +0x68)
247 | #define SPI_W11(i) (REG_SPI_BASE(i) +0x6C)
248 | #define SPI_W12(i) (REG_SPI_BASE(i) +0x70)
249 | #define SPI_W13(i) (REG_SPI_BASE(i) +0x74)
250 | #define SPI_W14(i) (REG_SPI_BASE(i) +0x78)
251 | #define SPI_W15(i) (REG_SPI_BASE(i) +0x7C)
252 |
253 | // +0x80 to +0xBC could be SPI_W16 through SPI_W31?
254 |
255 | // +0xC0 to +0xEC not currently defined.
256 |
257 | #define SPI_EXT0(i) (REG_SPI_BASE(i) + 0xF0) //From previous SDK. Removed _FLASH_ from name to match other registers.
258 | #define SPI_T_PP_ENA (BIT(31)) //From previous SDK
259 | #define SPI_T_PP_SHIFT 0x0000000F //From previous SDK
260 | #define SPI_T_PP_SHIFT_S 16 //From previous SDK
261 | #define SPI_T_PP_TIME 0x00000FFF //From previous SDK
262 | #define SPI_T_PP_TIME_S 0 //From previous SDK
263 |
264 | #define SPI_EXT1(i) (REG_SPI_BASE(i) + 0xF4) //From previous SDK. Removed _FLASH_ from name to match other registers.
265 | #define SPI_T_ERASE_ENA (BIT(31)) //From previous SDK
266 | #define SPI_T_ERASE_SHIFT 0x0000000F //From previous SDK
267 | #define SPI_T_ERASE_SHIFT_S 16 //From previous SDK
268 | #define SPI_T_ERASE_TIME 0x00000FFF //From previous SDK
269 | #define SPI_T_ERASE_TIME_S 0 //From previous SDK
270 |
271 | #define SPI_EXT2(i) (REG_SPI_BASE(i) + 0xF8) //From previous SDK. Removed _FLASH_ from name to match other registers.
272 | #define SPI_ST 0x00000007 //From previous SDK
273 | #define SPI_ST_S 0 //From previous SDK
274 |
275 | #define SPI_EXT3(i) (REG_SPI_BASE(i) + 0xFC)
276 | #define SPI_INT_HOLD_ENA 0x00000003
277 | #define SPI_INT_HOLD_ENA_S 0
278 | #endif // SPI_REGISTER_H_INCLUDED
279 |
--------------------------------------------------------------------------------
/software/spi.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2015 David Ogilvy (MetalPhreak)
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | * SOFTWARE.
23 | */
24 |
25 |
26 | #include "spi.h"
27 |
28 |
29 | ////////////////////////////////////////////////////////////////////////////////
30 | //
31 | // Function Name: spi_init
32 | // Description: Wrapper to setup HSPI/SPI GPIO pins and default SPI clock
33 | // Parameters: spi_no - SPI (0) or HSPI (1)
34 | //
35 | ////////////////////////////////////////////////////////////////////////////////
36 |
37 | void spi_init(uint8 spi_no){
38 |
39 | if(spi_no > 1) return; //Only SPI and HSPI are valid spi modules.
40 |
41 | spi_init_gpio(spi_no, SPI_CLK_USE_DIV);
42 | spi_clock(spi_no, SPI_CLK_PREDIV, SPI_CLK_CNTDIV);
43 | spi_tx_byte_order(spi_no, SPI_BYTE_ORDER_HIGH_TO_LOW);
44 | spi_rx_byte_order(spi_no, SPI_BYTE_ORDER_HIGH_TO_LOW);
45 |
46 | SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_CS_SETUP|SPI_CS_HOLD);
47 | CLEAR_PERI_REG_MASK(SPI_USER(spi_no), SPI_FLASH_MODE);
48 |
49 | }
50 |
51 | ////////////////////////////////////////////////////////////////////////////////
52 |
53 | ////////////////////////////////////////////////////////////////////////////////
54 | //
55 | // Function Name: spi_mode
56 | // Description: Configures SPI mode parameters for clock edge and clock polarity.
57 | // Parameters: spi_no - SPI (0) or HSPI (1)
58 | // spi_cpha - (0) Data is valid on clock leading edge
59 | // (1) Data is valid on clock trailing edge
60 | // spi_cpol - (0) Clock is low when inactive
61 | // (1) Clock is high when inactive
62 | //
63 | ////////////////////////////////////////////////////////////////////////////////
64 |
65 | void spi_mode(uint8 spi_no, uint8 spi_cpha,uint8 spi_cpol){
66 | if(spi_cpha) {
67 | CLEAR_PERI_REG_MASK(SPI_USER(spi_no), SPI_CK_OUT_EDGE);
68 | } else {
69 | SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_CK_OUT_EDGE);
70 | }
71 |
72 | if (spi_cpol) {
73 | SET_PERI_REG_MASK(SPI_PIN(spi_no), SPI_IDLE_EDGE);
74 | } else {
75 | CLEAR_PERI_REG_MASK(SPI_PIN(spi_no), SPI_IDLE_EDGE);
76 | }
77 | }
78 |
79 |
80 | ////////////////////////////////////////////////////////////////////////////////
81 |
82 | ////////////////////////////////////////////////////////////////////////////////
83 | //
84 | // Function Name: spi_init_gpio
85 | // Description: Initialises the GPIO pins for use as SPI pins.
86 | // Parameters: spi_no - SPI (0) or HSPI (1)
87 | // sysclk_as_spiclk - SPI_CLK_80MHZ_NODIV (1) if using 80MHz
88 | // sysclock for SPI clock.
89 | // SPI_CLK_USE_DIV (0) if using divider to
90 | // get lower SPI clock speed.
91 | //
92 | ////////////////////////////////////////////////////////////////////////////////
93 |
94 | void spi_init_gpio(uint8 spi_no, uint8 sysclk_as_spiclk){
95 |
96 | // if(spi_no > 1) return; //Not required. Valid spi_no is checked with if/elif below.
97 |
98 | uint32 clock_div_flag = 0;
99 | if(sysclk_as_spiclk){
100 | clock_div_flag = 0x0001;
101 | }
102 |
103 | if(spi_no==SPI){
104 | WRITE_PERI_REG(PERIPHS_IO_MUX, 0x005|(clock_div_flag<<8)); //Set bit 8 if 80MHz sysclock required
105 | PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_CLK_U, 1);
106 | PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_CMD_U, 1);
107 | PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_DATA0_U, 1);
108 | PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_DATA1_U, 1);
109 | }else if(spi_no==HSPI){
110 | WRITE_PERI_REG(PERIPHS_IO_MUX, 0x105|(clock_div_flag<<9)); //Set bit 9 if 80MHz sysclock required
111 | PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDI_U, 2); //GPIO12 is HSPI MISO pin (Master Data In)
112 | PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTCK_U, 2); //GPIO13 is HSPI MOSI pin (Master Data Out)
113 | PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTMS_U, 2); //GPIO14 is HSPI CLK pin (Clock)
114 | PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDO_U, 2); //GPIO15 is HSPI CS pin (Chip Select / Slave Select)
115 | }
116 |
117 | }
118 |
119 | ////////////////////////////////////////////////////////////////////////////////
120 |
121 | ////////////////////////////////////////////////////////////////////////////////
122 | //
123 | // Function Name: spi_clock
124 | // Description: sets up the control registers for the SPI clock
125 | // Parameters: spi_no - SPI (0) or HSPI (1)
126 | // prediv - predivider value (actual division value)
127 | // cntdiv - postdivider value (actual division value)
128 | // Set either divider to 0 to disable all division (80MHz sysclock)
129 | //
130 | ////////////////////////////////////////////////////////////////////////////////
131 |
132 | void spi_clock(uint8 spi_no, uint16 prediv, uint8 cntdiv){
133 |
134 | if(spi_no > 1) return;
135 |
136 | if((prediv==0)|(cntdiv==0)){
137 |
138 | WRITE_PERI_REG(SPI_CLOCK(spi_no), SPI_CLK_EQU_SYSCLK);
139 |
140 | } else {
141 |
142 | WRITE_PERI_REG(SPI_CLOCK(spi_no),
143 | (((prediv-1)&SPI_CLKDIV_PRE)<>1)&SPI_CLKCNT_H)< 1) return;
174 |
175 | if(byte_order){
176 | SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_WR_BYTE_ORDER);
177 | } else {
178 | CLEAR_PERI_REG_MASK(SPI_USER(spi_no), SPI_WR_BYTE_ORDER);
179 | }
180 | }
181 | ////////////////////////////////////////////////////////////////////////////////
182 |
183 | ////////////////////////////////////////////////////////////////////////////////
184 | //
185 | // Function Name: spi_rx_byte_order
186 | // Description: Setup the byte order for shifting data into buffer
187 | // Parameters: spi_no - SPI (0) or HSPI (1)
188 | // byte_order - SPI_BYTE_ORDER_HIGH_TO_LOW (1)
189 | // Data is read in starting with Bit31 and down to Bit0
190 | //
191 | // SPI_BYTE_ORDER_LOW_TO_HIGH (0)
192 | // Data is read in starting with the lowest BYTE, from
193 | // MSB to LSB, followed by the second lowest BYTE, from
194 | // MSB to LSB, followed by the second highest BYTE, from
195 | // MSB to LSB, followed by the highest BYTE, from MSB to LSB
196 | // 0xABCDEFGH would be read as 0xGHEFCDAB
197 | //
198 | //
199 | ////////////////////////////////////////////////////////////////////////////////
200 |
201 | void spi_rx_byte_order(uint8 spi_no, uint8 byte_order){
202 |
203 | if(spi_no > 1) return;
204 |
205 | if(byte_order){
206 | SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_RD_BYTE_ORDER);
207 | } else {
208 | CLEAR_PERI_REG_MASK(SPI_USER(spi_no), SPI_RD_BYTE_ORDER);
209 | }
210 | }
211 | ////////////////////////////////////////////////////////////////////////////////
212 |
213 | ////////////////////////////////////////////////////////////////////////////////
214 | //
215 | // Function Name: spi_transaction
216 | // Description: SPI transaction function
217 | // Parameters: spi_no - SPI (0) or HSPI (1)
218 | // cmd_bits - actual number of bits to transmit
219 | // cmd_data - command data
220 | // addr_bits - actual number of bits to transmit
221 | // addr_data - address data
222 | // dout_bits - actual number of bits to transmit
223 | // dout_data - output data
224 | // din_bits - actual number of bits to receive
225 | //
226 | // Returns: read data - uint32 containing read in data only if RX was set
227 | // 0 - something went wrong (or actual read data was 0)
228 | // 1 - data sent ok (or actual read data is 1)
229 | // Note: all data is assumed to be stored in the lower bits of
230 | // the data variables (for anything <32 bits).
231 | //
232 | ////////////////////////////////////////////////////////////////////////////////
233 |
234 | uint32 spi_transaction(uint8 spi_no, uint8 cmd_bits, uint16 cmd_data, uint32 addr_bits, uint32 addr_data, uint32 dout_bits, uint32 dout_data,
235 | uint32 din_bits, uint32 dummy_bits){
236 |
237 | if(spi_no > 1) return 0; //Check for a valid SPI
238 |
239 | //code for custom Chip Select as GPIO PIN here
240 |
241 | while(spi_busy(spi_no)); //wait for SPI to be ready
242 |
243 | //########## Enable SPI Functions ##########//
244 | //disable MOSI, MISO, ADDR, COMMAND, DUMMY in case previously set.
245 | CLEAR_PERI_REG_MASK(SPI_USER(spi_no), SPI_USR_MOSI|SPI_USR_MISO|SPI_USR_COMMAND|SPI_USR_ADDR|SPI_USR_DUMMY);
246 |
247 | //enable functions based on number of bits. 0 bits = disabled.
248 | //This is rather inefficient but allows for a very generic function.
249 | //CMD ADDR and MOSI are set below to save on an extra if statement.
250 | // if(cmd_bits) {SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_USR_COMMAND);}
251 | // if(addr_bits) {SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_USR_ADDR);}
252 | if(din_bits) {SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_USR_MISO);}
253 | if(dummy_bits) {SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_USR_DUMMY);}
254 | //########## END SECTION ##########//
255 |
256 | //########## Setup Bitlengths ##########//
257 | WRITE_PERI_REG(SPI_USER1(spi_no), ((addr_bits-1)&SPI_USR_ADDR_BITLEN)<>8)&0xff) | ((command<<8)&0xff00); //swap byte order
268 | WRITE_PERI_REG(SPI_USER2(spi_no), ((((cmd_bits-1)&SPI_USR_COMMAND_BITLEN)<>(32-(dout_bits - dout_extra_bits)))&dout_data));
298 | } else {
299 | WRITE_PERI_REG(SPI_W0(spi_no), dout_data);
300 | }
301 | }
302 | }
303 | //########## END SECTION ##########//
304 |
305 | //########## Begin SPI Transaction ##########//
306 | SET_PERI_REG_MASK(SPI_CMD(spi_no), SPI_USR);
307 | //########## END SECTION ##########//
308 |
309 | //########## Return DIN data ##########//
310 | if(din_bits) {
311 | while(spi_busy(spi_no)); //wait for SPI transaction to complete
312 |
313 | if(READ_PERI_REG(SPI_USER(spi_no))&SPI_RD_BYTE_ORDER) {
314 | return READ_PERI_REG(SPI_W0(spi_no)) >> (32-din_bits); //Assuming data in is written to MSB. TBC
315 | } else {
316 | return READ_PERI_REG(SPI_W0(spi_no)); //Read in the same way as DOUT is sent. Note existing contents of SPI_W0 remain unless overwritten!
317 | }
318 |
319 | return 0; //something went wrong
320 | }
321 | //########## END SECTION ##########//
322 |
323 | //Transaction completed
324 | return 1; //success
325 | }
326 |
327 | ////////////////////////////////////////////////////////////////////////////////
328 |
329 | /*///////////////////////////////////////////////////////////////////////////////
330 | //
331 | // Function Name: func
332 | // Description:
333 | // Parameters:
334 | //
335 | ////////////////////////////////////////////////////////////////////////////////
336 |
337 | void func(params){
338 |
339 | }
340 |
341 | ///////////////////////////////////////////////////////////////////////////////*/
342 |
343 |
--------------------------------------------------------------------------------
/software/Q1.ino:
--------------------------------------------------------------------------------
1 | #include "Q1.h"
2 | #include "pins.h"
3 |
4 | #include "spi.h"
5 |
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include
14 | #include
15 | #include
16 | #include
17 | #include
18 |
19 |
20 | Ticker refresh;
21 | WebSocketsServer webSocket = WebSocketsServer(81);
22 | ESP8266WebServer server(80);
23 | File fsUploadFile;
24 |
25 | uint8_t APConnectMode = 0;
26 |
27 | char AP_SSID[34] = "";
28 | char AP_password[65] = "";
29 |
30 | /* Soft AP network parameters */
31 | IPAddress apIP(192, 168, 4, 1);
32 | IPAddress netMsk(255, 255, 255, 0);
33 | char station_password[65] = "";
34 | char station_SSID[33] = "";
35 |
36 | // DNS server
37 | const byte DNS_PORT = 53;
38 | DNSServer dnsServer;
39 |
40 | int readValue = 0;
41 |
42 | uint16_t minCal = 0;
43 | uint16_t threshCal = 1023;
44 | uint16_t maxCal = 1023;
45 | uint16_t rxFrequency = 0;
46 | uint8_t minPulse = 0;
47 |
48 |
49 | pulseData recordedPulses[100];
50 | int recordedPulsesIndex = 0;
51 |
52 | bool samplerRunning = false;
53 | bool sessionRunning = false;
54 | bool pulseActive = false;
55 | unsigned long pulseStart = 0;
56 | unsigned long pulseEnd = 0;
57 | unsigned long pulseEndLast = 0;
58 | unsigned long lastSampleTime =0;
59 |
60 | unsigned long connectingTime = 0;
61 |
62 | unsigned long timerZero = 0;
63 | int lapCount = 0;
64 | unsigned long lastLapTime = 0;
65 |
66 | bool blinkFlag = false;
67 | bool sendSampleFlag = false;
68 |
69 | void setup() {
70 | Serial.begin(115200);
71 | delay(100);
72 |
73 | pinMode(BLINK_PIN, OUTPUT);
74 | digitalWrite(BLINK_PIN, HIGH);
75 | pinMode(BEEP_PIN, OUTPUT);
76 | digitalWrite(BEEP_PIN, LOW);
77 |
78 | pinMode(SETTINGS_RESET_PIN, INPUT_PULLUP);
79 |
80 | spi_init(HSPI);
81 | // RX datasheet says to use leading clock, but with hardware SPI it only seems to work with trailing
82 | spi_mode(HSPI, 1, 0); // trailing clock, low idle
83 |
84 | SPIFFS.begin();
85 |
86 | if ( !digitalRead( SETTINGS_RESET_PIN) ) {
87 | Serial.println();
88 | Serial.println("settings reset requested");
89 | resetSettings();
90 | }
91 |
92 | // power down parts of the RX we're not using
93 | uint32_t registerValue = 0;
94 | registerValue = PD_VCLAMP | PD_VAMP | PD_IFAF | PD_DIV4 | PD_5GVCO | PD_AU6M | PD_6M | PD_AU6M5 | PD_6M5 | PD_REG1D8 | PD_DIV80 | PD_PLL1D8;
95 | vtxWrite(0x0A, registerValue);
96 |
97 | loadSettings();
98 |
99 |
100 | WiFi.mode(WIFI_AP_STA);
101 |
102 | /* Setup the DNS server redirecting all the domains to the apIP */
103 | Serial.println("Starting DNS ");
104 | dnsServer.setErrorReplyCode(DNSReplyCode::NoError);
105 | dnsServer.start(DNS_PORT, "*", apIP);
106 |
107 | Serial.println("Starting softAP ");
108 | WiFi.softAPConfig(apIP, apIP, netMsk);
109 | WiFi.softAP(station_SSID, station_password);
110 |
111 | if (APConnectMode == AP_AUTOCONNECT){
112 | Serial.print("Connecting to ");
113 | Serial.println(AP_SSID);
114 | WiFi.begin(AP_SSID, AP_password);
115 | connectingTime = millis();
116 | while (WiFi.status() != WL_CONNECTED) {
117 | if ((millis() - connectingTime) > 10000) {
118 | WiFi.disconnect();
119 | break;
120 | }
121 | delay(500);
122 | Serial.print(".");
123 | }
124 |
125 | if (WiFi.status() == WL_CONNECTED) {
126 | Serial.println("");
127 | Serial.println("WiFi connected");
128 | Serial.println("IP address: ");
129 | Serial.println(WiFi.localIP());
130 | }
131 | } else {
132 | WiFi.disconnect();
133 | }
134 |
135 | ArduinoOTA.setHostname("");
136 | ArduinoOTA.onStart([]() {
137 | Serial.println("Start");
138 | });
139 | ArduinoOTA.onEnd([]() {
140 | Serial.println("\nEnd");
141 | });
142 | ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
143 | Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
144 | });
145 | ArduinoOTA.onError([](ota_error_t error) {
146 | Serial.printf("Error[%u]: ", error);
147 | if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
148 | else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
149 | else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
150 | else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
151 | else if (error == OTA_END_ERROR) Serial.println("End Failed");
152 | });
153 | ArduinoOTA.begin();
154 |
155 | webSocket.begin();
156 | webSocket.onEvent(webSocketEvent);
157 |
158 | //SERVER INIT
159 | //called when the url is not defined here
160 | //use it to load content from SPIFFS
161 | server.onNotFound([](){
162 | if(!handleFileRead(server.uri()))
163 | server.send(404, "text/plain", "FileNotFound");
164 | });
165 | server.begin();
166 |
167 |
168 | }
169 |
170 | void loop() {
171 | dnsServer.processNextRequest();
172 | ArduinoOTA.handle();
173 | webSocket.loop();
174 | server.handleClient();
175 | lapTimerLoop();
176 | if (blinkFlag){
177 | blinkFlag=false;
178 | doBlink();
179 | }
180 | if (sendSampleFlag){
181 | sendSampleFlag=false;
182 | char buffer[7];
183 | sprintf(buffer, "a%i", readValue);
184 | webSocket.broadcastTXT(buffer);
185 | }
186 | }
187 |
188 | void resetSettings(){
189 |
190 | uint8_t mac[WL_MAC_ADDR_LENGTH];
191 | WiFi.softAPmacAddress(mac);
192 | char buffer[20];
193 | sprintf(buffer, "Q1 node - %X%X", mac[WL_MAC_ADDR_LENGTH-2], mac[WL_MAC_ADDR_LENGTH-1] );
194 |
195 | EEPROM.begin(256);
196 | for (int i = 0; i < 256; i++) EEPROM.write(i, 0);
197 | EEPROM.put(0, "Q1Settings");
198 | EEPROM.put(10, 0); // settings version number
199 | EEPROM.put(11, AP_CONNECT_DISABLED); // ap connect mode
200 | EEPROM.put(12, 5704); // rx frequency
201 | EEPROM.put(14, 600); // low calibrate
202 | EEPROM.put(16, 1024); // mid calibrate
203 | EEPROM.put(18, 1024); // high calibrate
204 | EEPROM.put(20, buffer); // station SSID
205 | EEPROM.put(52, "12345678"); // station password
206 | EEPROM.put(116, ""); // ap SSID
207 | EEPROM.put(148, ""); // ap password
208 | EEPROM.put(212, 15); // ap password
209 |
210 | EEPROM.end();
211 | }
212 |
213 | void saveSettings(){
214 |
215 | EEPROM.begin(256);
216 |
217 | EEPROM.put(11, APConnectMode); // ap connect mode
218 | EEPROM.put(12, rxFrequency); // rx frequency
219 | EEPROM.put(14, minCal); // low calibrate
220 | EEPROM.put(16, threshCal); // mid calibrate
221 | EEPROM.put(18, maxCal); // high calibrate
222 | EEPROM.put(20, station_SSID); // station SSID
223 | EEPROM.put(52, station_password); // station password
224 | EEPROM.put(116, AP_SSID); // ap SSID
225 | EEPROM.put(148, AP_password); // ap password
226 | EEPROM.put(212, minPulse); // ap password
227 |
228 | // next byte 213
229 |
230 | EEPROM.end();
231 | }
232 |
233 | void sendSettings(uint8_t num){
234 | char buffer[128];
235 |
236 | sprintf(buffer, "dAPMode:%i", APConnectMode );
237 | webSocket.sendTXT(num, buffer);
238 | sprintf(buffer, "dfreq:%i", rxFrequency );
239 | webSocket.sendTXT(num, buffer);
240 | sprintf(buffer, "dminCal:%i", minCal );
241 | webSocket.sendTXT(num, buffer);
242 | sprintf(buffer, "dthreshCal:%i", threshCal );
243 | webSocket.sendTXT(num, buffer);
244 | sprintf(buffer, "dmaxCal:%i", maxCal );
245 | webSocket.sendTXT(num, buffer);
246 | sprintf(buffer, "dstationSSID:%s", station_SSID );
247 | webSocket.sendTXT(num, buffer);
248 | sprintf(buffer, "dstationPass:%s", station_password );
249 | webSocket.sendTXT(num, buffer);
250 | sprintf(buffer, "dAPSSID:%s", AP_SSID );
251 | webSocket.sendTXT(num, buffer);
252 | sprintf(buffer, "dAPPass:%s", AP_password );
253 | webSocket.sendTXT(num, buffer);
254 | sprintf(buffer, "dminPulse:%i", minPulse );
255 | webSocket.sendTXT(num, buffer);
256 | }
257 |
258 | void sendStatus(uint8_t num){
259 | char buffer[128];
260 | sprintf(buffer, "dAPstatus:%i", WiFi.status() );
261 | webSocket.sendTXT(num, buffer);
262 | sprintf(buffer, "dminCal:%i", minCal );
263 | webSocket.sendTXT(num, buffer);
264 | sprintf(buffer, "dthreshCal:%i", threshCal );
265 | webSocket.sendTXT(num, buffer);
266 | sprintf(buffer, "dmaxCal:%i", maxCal );
267 | webSocket.sendTXT(num, buffer);
268 | sprintf(buffer, "dsessionRunning:%i", sessionRunning );
269 | webSocket.sendTXT(num, buffer);
270 | if (WiFi.status() == WL_CONNECTED) {
271 | sprintf(buffer, "dAPIP:%d.%d.%d.%d", WiFi.localIP()[0], WiFi.localIP()[1], WiFi.localIP()[2], WiFi.localIP()[3] );
272 | webSocket.sendTXT(num, buffer);
273 | }
274 | }
275 |
276 | void loadSettings(){
277 | EEPROM.begin(256);
278 |
279 | char headMessage[12] = "";
280 | uint8_t settingsVersion = 0;
281 |
282 | for (int i = 0; i < 11; i++){
283 | headMessage[i] = EEPROM.read(i);
284 | }
285 | headMessage[11] = '\0';
286 |
287 | EEPROM.get( 10, settingsVersion );
288 | EEPROM.get( 11, APConnectMode );
289 | EEPROM.get( 12, rxFrequency );
290 | setVTXChannel(rxFrequency);
291 |
292 | EEPROM.get( 14, minCal );
293 | EEPROM.get( 16, threshCal );
294 | EEPROM.get( 18, maxCal );
295 |
296 | for (int i = 0; i < 32; i++){
297 | station_SSID[i] = EEPROM.read(i+20);
298 | }
299 | station_SSID[32] = '\0';
300 | for (int i = 0; i < 64; i++){
301 | station_password[i] = EEPROM.read(i+52);
302 | }
303 | station_password[64] = '\0';
304 |
305 |
306 | for (int i = 0; i < 32; i++){
307 | AP_SSID[i] = EEPROM.read(i+116);
308 | }
309 | AP_SSID[32] = '\0';
310 | for (int i = 0; i < 64; i++){
311 | AP_password[i] = EEPROM.read(i+148);
312 | }
313 | AP_password[64] = '\0';
314 |
315 | EEPROM.get( 212, minPulse );
316 | EEPROM.end();
317 |
318 | if (strcmp(headMessage, "Q1Settings") != 0) {
319 | Serial.println("invalid settings EEPROM, resetting");
320 | resetSettings();
321 | loadSettings();
322 | return;
323 | }
324 |
325 | }
326 |
327 | String getContentType(String filename){
328 | if(server.hasArg("download")) return "application/octet-stream";
329 | else if(filename.endsWith(".htm")) return "text/html";
330 | else if(filename.endsWith(".html")) return "text/html";
331 | else if(filename.endsWith(".css")) return "text/css";
332 | else if(filename.endsWith(".js")) return "application/javascript";
333 | else if(filename.endsWith(".png")) return "image/png";
334 | else if(filename.endsWith(".gif")) return "image/gif";
335 | else if(filename.endsWith(".jpg")) return "image/jpeg";
336 | else if(filename.endsWith(".ico")) return "image/x-icon";
337 | else if(filename.endsWith(".xml")) return "text/xml";
338 | else if(filename.endsWith(".pdf")) return "application/x-pdf";
339 | else if(filename.endsWith(".zip")) return "application/x-zip";
340 | else if(filename.endsWith(".gz")) return "application/x-gzip";
341 | return "text/plain";
342 | }
343 |
344 | bool handleFileRead(String path){
345 | Serial.println("handleFileRead: " + path);
346 | if(path.endsWith("/")) path += "mobile.html";
347 | String contentType = getContentType(path);
348 | String pathWithGz = path + ".gz";
349 | if(SPIFFS.exists(pathWithGz) || SPIFFS.exists(path)){
350 | if(SPIFFS.exists(pathWithGz))
351 | path += ".gz";
352 | File file = SPIFFS.open(path, "r");
353 | size_t sent = server.streamFile(file, contentType);
354 | file.close();
355 | return true;
356 | }
357 | return false;
358 | }
359 |
360 |
361 | void lapTimerLoop(){
362 | unsigned long currTime = 0;
363 |
364 | if (samplerRunning){
365 | currTime = getLapTime();
366 | if ((currTime - lastSampleTime) > 2) {
367 | readValue = getRSSI();
368 | lastSampleTime = currTime;
369 | }
370 | }
371 |
372 | if (sessionRunning){
373 | if (readValue >= convertRange(threshCal) ){
374 |
375 | if (!pulseActive){
376 | pulseActive = true;
377 | pulseStart = currTime; // more than a single sample drop, start a new pulse
378 | }
379 |
380 | } else {
381 | if (pulseActive){
382 | pulseEnd = currTime;
383 | pulseActive = false;
384 | recordedPulses[recordedPulsesIndex].start = pulseStart;
385 | recordedPulses[recordedPulsesIndex].end = pulseEnd;
386 | recordedPulsesIndex++;
387 |
388 | //unsigned long pulseLength = currTime-pulseStart;
389 |
390 | //char buffer[128];
391 | //sprintf(buffer, "c%i, %i", (currTime-pulseStart)/1000, (pulseStart-pulseEndLast)/1000 );
392 | //webSocket.sendTXT(0, buffer);
393 |
394 | pulseEndLast = pulseEnd;
395 |
396 | }
397 | }
398 |
399 | if ( ((currTime - pulseEndLast) >1000) && (recordedPulsesIndex > 0) && !pulseActive ){
400 | pulseData expandedPulses[recordedPulsesIndex];
401 | for (int i = 0; i< recordedPulsesIndex;i++){
402 | unsigned long newStart = 0;
403 | unsigned long newEnd = 0;
404 | unsigned long expand = 0;
405 | expand = (recordedPulses[i].end - recordedPulses[i].start) * 0.1;
406 | expandedPulses[i].start = recordedPulses[i].start - expand;
407 | expandedPulses[i].end = recordedPulses[i].end + expand;
408 |
409 | }
410 |
411 | pulseData mergedPulses[recordedPulsesIndex];
412 | int mergedIndex = 0;
413 | bool merged = false;
414 |
415 | for (int i = 0; i< recordedPulsesIndex;i++){
416 | for (int j = 0; j< mergedIndex;j++){
417 | if( mergedPulses[j].end > expandedPulses[i].start ) {
418 | mergedPulses[j].start = _min(mergedPulses[j].start, expandedPulses[i].start );
419 | mergedPulses[j].end = _max(mergedPulses[j].end, expandedPulses[i].end );
420 | mergedIndex = j+1;
421 | merged = true;
422 | }
423 |
424 | }
425 | if (!merged){
426 | mergedPulses[mergedIndex].start = recordedPulses[i].start;
427 | mergedPulses[mergedIndex].end = recordedPulses[i].end;
428 | mergedIndex++;
429 | } else {
430 | merged = false;
431 | }
432 |
433 | }
434 |
435 | unsigned long maxLength = 0;
436 | int maxIndex = 0;
437 | for (int i = 0; i< mergedIndex;i++){
438 | unsigned long tempLength = mergedPulses[i].end - mergedPulses[i].start;
439 | if ( tempLength > maxLength ) {
440 | maxLength = tempLength;
441 | maxIndex = i;
442 | }
443 | }
444 |
445 | if (maxLength > minPulse) { // if the pulse time was too short just throw it away
446 | unsigned long offsetTime = getLapTime();
447 | digitalWrite(BLINK_PIN, LOW);
448 | digitalWrite(BEEP_PIN, HIGH);
449 | delay(250);
450 | digitalWrite(BLINK_PIN, HIGH);
451 | digitalWrite(BEEP_PIN, LOW);
452 |
453 | unsigned long lapTime = (mergedPulses[maxIndex].end + mergedPulses[maxIndex].start ) / 2;
454 | int offset = offsetTime - lapTime ;
455 | char buffer[128];
456 | char timecodeStr[16];
457 | char lapTimeStr[16];
458 | dtostrf(lapTime/1000.0, 4, 2, timecodeStr);
459 | dtostrf((lapTime-lastLapTime)/1000.0, 4, 2, lapTimeStr);
460 |
461 | sprintf(buffer, "cL%i,%s,%s,%i,%i", lapCount, timecodeStr, lapTimeStr, maxLength, offset );
462 | webSocket.broadcastTXT(buffer);
463 |
464 | lastLapTime = lapTime;
465 | lapCount++;
466 | }
467 | /*
468 | char buffer[128];
469 | sprintf(buffer, "clap %i of %i(%i), %i", maxIndex, mergedIndex, recordedPulsesIndex, maxLength/1000 );
470 | webSocket.broadcastTXT(buffer);
471 | for (int i = 0; i< mergedIndex;i++){
472 | delay(0);
473 | sprintf(buffer, "c%i, %i", i, (mergedPulses[i].end - mergedPulses[i].start ) /1000 );
474 |
475 | webSocket.broadcastTXT(buffer);
476 | }
477 | */
478 |
479 | //reset for the next lap
480 | recordedPulsesIndex = 0;
481 | }
482 | }
483 | }
484 |
485 | void calibrateHigh(){
486 | int avg = analogAverage(1000);
487 | Serial.print(avg);
488 | Serial.println( " high.");
489 | if (avg <= minCal) avg = minCal + 1;
490 | maxCal = avg;
491 | saveSettings();
492 | }
493 | void calibrateThreshold(){
494 | int avg = analogAverage(1000);
495 | Serial.print(avg);
496 | Serial.println( " thresh.");
497 | threshCal = avg;
498 | saveSettings();
499 | }
500 |
501 | void calibrateLow(){
502 | int avg = analogAverage(1000);
503 | Serial.print(avg);
504 | Serial.println( " low.");
505 | if (avg >= minCal) avg = maxCal - 1;
506 | minCal = avg;
507 | saveSettings();
508 | }
509 |
510 | int analogAverage(int samples){
511 | long acu = 0;
512 | for (int i=0; i 32) {
656 | //error case
657 | return;
658 | }
659 | strcpy( AP_SSID, payloadData);
660 | saveSettings();
661 | }else if (strncmp((char*)payload, "sapp", 4) == 0) {
662 | // set the AP password
663 | char *payloadData = (char*)payload + 4;
664 | if ( strlen(payloadData) > 64) {
665 | //error case
666 | return;
667 | }
668 | strcpy( AP_password, payloadData);
669 | saveSettings();
670 | }else if (strncmp((char*)payload, "sapm", 4) == 0) {
671 | // set the AP mode
672 | char *payloadData = (char*)payload + 4;
673 | int mode = 0;
674 | mode = atoi(payloadData);
675 | APConnectMode = mode;
676 | Serial.print("set mode ");
677 | Serial.println(mode);
678 | saveSettings();
679 | }else if (strncmp((char*)payload, "sstn", 4) == 0) {
680 | // set the station password
681 | char *payloadData = (char*)payload + 4;
682 | if ( strlen(payloadData) > 32) {
683 | //error case
684 | return;
685 | }
686 | strcpy( station_SSID, payloadData);
687 | saveSettings();
688 | }else if (strncmp((char*)payload, "sstp", 4) == 0) {
689 | // set the station password
690 | char *payloadData = (char*)payload + 4;
691 | if ( strlen(payloadData) > 64) {
692 | //error case
693 | return;
694 | }
695 | strcpy( station_password, payloadData);
696 | saveSettings();
697 | } else {
698 | webSocket.sendTXT(0, "pong");
699 | }
700 |
701 | // send message to client
702 | // webSocket.sendTXT(num, "message here");
703 |
704 | // send data to all connected clients
705 | // webSocket.broadcastTXT("message here");
706 | break;
707 | case WStype_BIN:
708 | Serial.printf("[%u] get binary lenght: %u\n", num, lenght);
709 | hexdump(payload, lenght);
710 |
711 | // send message to client
712 | // webSocket.sendBIN(num, payload, lenght);
713 | break;
714 | }
715 |
716 | }
717 |
718 |
719 | uint16_t setVTXChannel(uint16_t channel)
720 | {
721 | if (channel > 5950) channel = 5950;
722 | if (channel < 5630) channel = 5630;
723 |
724 | uint32_t registerValue = 0;
725 | registerValue = getChannelValueFromFreq(channel);
726 | vtxWrite(0x1, registerValue);
727 | return(channel);
728 | }
729 |
730 | uint32_t getChannelValueFromFreq(uint16_t frequency){
731 | //get the register value for the supplied frequency (in mhz)
732 | //frequency=2*(N*32+A)
733 | uint16_t nTerm = 0;
734 | uint16_t aTerm = 0;
735 | uint32_t registerValue = 0;
736 |
737 | nTerm = ((frequency-479)/2)/32;
738 | aTerm = ((frequency-479)/2)-(nTerm*32);
739 |
740 | registerValue = (nTerm<<7) | (aTerm);
741 | return(registerValue);
742 | }
743 |
744 | void vtxWrite(uint8_t address, uint32_t data){
745 | // pack a buffer to be written to the vtx
746 | // VTX expects LS bit first, so we assemble the buffer in reverse
747 | // so when the SPI hardware sends it MS bit first it will come in the right order
748 | uint32_t sendBuffer = 0;
749 | uint32_t temp = 0;
750 |
751 | // assemble register address
752 | temp = address;
753 | for (uint8_t i = 0; i < 4; i++)
754 | {
755 | sendBuffer <<= 1;
756 | sendBuffer = sendBuffer| (temp & 0x1);
757 | temp >>= 1;
758 | }
759 |
760 | // set write bit (we're always in write mode)
761 | sendBuffer <<= 1;
762 | sendBuffer = sendBuffer| 0x1;
763 |
764 | // assemble register data
765 | temp = data;
766 | for (uint8_t i = 0; i < 20; i++)
767 | {
768 | sendBuffer <<= 1;
769 | sendBuffer = sendBuffer| (temp & 0x1);
770 | temp >>= 1;
771 | }
772 |
773 | // send command to VTX
774 |
775 | spi_txd(HSPI, 25, sendBuffer);
776 |
777 | }
778 |
--------------------------------------------------------------------------------