├── 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 | 18 | 19 | 20 |
21 | 22 | Status: - Idle 23 | 24 | 25 |
26 | 27 | 28 | 29 |
30 |
31 | 32 |
33 |
34 |
35 | 36 |
37 | 38 | 39 | 56 | 57 | 58 | 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 | 235 | 240 | 241 |
231 |
Log:
232 | 234 |
236 |
Time:
237 | 239 |
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 | --------------------------------------------------------------------------------