first commit

This commit is contained in:
Xavier 2022-08-24 08:28:06 +02:00
commit cab552d78c
80 changed files with 25407 additions and 0 deletions

BIN
Display.pdf Executable file

Binary file not shown.

91
README.md Executable file
View File

@ -0,0 +1,91 @@
RadioSonde Version 0.8.5
============================
<img src="http://xavier.debert.free.fr/RS/TTGO.jpg" width="50%"><img src="http://xavier.debert.free.fr/RS/TTGO2.jpg" width="50%">
<img src="http://xavier.debert.free.fr/RS/TTGO3.jpg" width="50%"><img src="http://xavier.debert.free.fr/RS/TTGO4.jpg" width="50%"><img src="http://xavier.debert.free.fr/RS/TTGO5.jpg" width="50%">
<img src="http://xavier.debert.free.fr/RS/Web4.png" width="20%"><img src="http://xavier.debert.free.fr/RS/Web.png" width="20%">
<img src="http://xavier.debert.free.fr/RS/Web3.png" width="20%"><img src="http://xavier.debert.free.fr/RS/Web2.png" width="20%">
<img src="http://xavier.debert.free.fr/RS/Web5.png" width="20%">
Projet basé sur le travail de DL9RDZ
====================================
Pour TTGO LORA 32 esp32 pico D4
Décodage de RadioSonde RS41 and DFM06/09 et M10
Attention à la version de votre TTGO!
vous devez modifier dans config.txt, le port de l'écran OLED
- TTGO v1: SDA=4 SCL=15, RST=16
- TTGO v2: SDA=21 SCL=22, RST=16
## Version en production 0.8.5 et devel 0.8.7
## 0.8.0
travail de refonte et réécriture du code
## 0.8.1
modification de la partie Web
## 0.8.5
Evolution majeur du système
affichage du pourcentage de la batterie en mode scanning
création d'une fenetre Batterie, Boussole
suppresion lib et code TFT
création Azimute, elevation correction de Bugs majeur , mineur
Ajout fonction Smetre, Buzzer, QTH, Gps on off ...
mise à jour OTA
trop de modification pour toutes les expliciter!
## Les Boutons optionnel à ajouter(souder)
sur les GPIO 1002 et 1004
attention:
+5V--[ SW ]---GPIO----[ R1 ]---/ R1=10 ou 12KOhms
- appuie court <1.5 seconds
- appuie double court 0.5 seconds
- appuie moyen 2-4 seconds
- appuie long >5 seconds
## Wifi configuration
Au démarrage, si aucune connexion possible au wifi paramètré, il monte un Wifi AP
le SSID et mot de passe par défaut est: Radiosonde
en mode AP, il doit être en 192.168.4.1,
mais vous avez aussi la possibilité de mettre http://radiosonde.local dans n'importe quel Wifi
connecté.
## Mode Scanne
Le fichier qrg.txt contient la liste par défaut des cannaux.
pour y configurer les noms, fréquences et mode [M=M10, 6=DFM06, 9=DFM09 et 4=RS41]
## Mode Réception
En réception, une seul fréquence est décodé, les infos de la sonde (ID, GPS, RSSI, ...)
seront affichées dans plusieur fenetre à choisir ( 0 à 6) à configurer dans la page Web
rubrique config.
In receiving mode, a single frequency will be decoded, and sonde info (ID, GPS
coordinates, RSSI) will be displayed. The bar above the IP address indicates,
for the last 18 frames, if reception was successfull (|) or failed (.)
A DOUBLE press will switch to scanning mode.
A SHORT press will switch to the next channel in channels.txt
## Mode Analyseur
Le mode analyseur de spectre (400..406 MHz) est affiché (chaque ligne == 50 kHz)
Pour les cartes TTGO sans bouton configurable, il y a un nouveau paramètre dans config.txt:
- spectrum=10 // 0=off / 1-99 nombre de seconds pour afficher l'analyseur
- timer=1 // 0=off / 1= afficher le compte à rebours du spectre dans l'affichage du spectre
- marker=1 // 0=off / 1= afficher la fréquence dans l'affichage du spectre
## Setup
voir Setup.md pour l'installation!
73
Xavier

2412
RadioSonde_FSK/RadioSonde_FSK.ino Executable file

File diff suppressed because it is too large Load Diff

109
RadioSonde_FSK/data/config.txt Executable file
View File

@ -0,0 +1,109 @@
#-------------------------------#
# Hardware depending settings
#-------------------------------#
# pin: 255=disabled; x=button x+128=touch button
button_pin=2
button2_pin=4
# No specification in config file: try autodetection (gpio4 pin level at startup)
#button_pin=0
#button2_pin=255
#button2_axp=0
# LED port
#led_pout=-1
# OLED Setup is depending on hardware of LoRa board
# TTGO v1: SDA=4 SCL=15, RST=16
# TTGO v2: SDA=21 SCL=22, RST=16
# T-BEAM, OLED: SDA=21, SCL=22, RST=16
# T-BEAM, ILI9225: SDA=4, CLK=21, RS=2, RST=22, CS=0
# No specification in config file: try autodetection (gpio4 pin level at startup)
#
# disptype: 0=OLED, 1=ILI9225
#disptype=0
#oled_sda=21
#oled_scl=22
#oled_rst=16
oled_orient=1
gpsOn=0
gps_rxd=-1
gps_txd=-1
gps_lat=43.59169
gps_lon=7.10071
gps_alt=123
# Show AFC value (for RS41 only, maybe also DFM, but useful for RS92 or M10)
# showafc=1
# Frequency correction, in Hz
# freqofs=0
#-------------------------------#
# General config settings
#-------------------------------#
maxsonde=20
debug=0
# wifi mode: 1=client in background; 2=AP in background; 3=client on startup, ap if failure
wifi=3
# TCP/IP KISS TNC in port 14590 for APRSdroid (0=disabled, 1=enabled)
kisstnc.active = 1
mdnsname=radiosonde
# display configuration. List of "displays"
# first entry: "Scanner" display
# second entry: default "Receiver" display
# additional entries: alternative receiver display, activated by button
display=0,1,2,3,4,5,6
# set to -1 to disable (used for "N" values in timers in screens.txt). Value in seconds
norx_timeout=20
vbatmax=1.86
vbatmin=1.64
sdOn=0
buzzerOn=0
buzzerPort=12
buzzerFreq=700
dbsmetre=1
#-------------------------------#
# Spectrum display settings
#-------------------------------#
startfreq=400
channelbw=10
spectrum=30 #10
noisefloor=-125
marker=1
#-------------------------------#
# APRS settings
#-------------------------------#
call=NOCALL
passcode=123456
#-------------------------------#
# Sonde specific settings: bandwidth
# valid values: 3100, 3900, 5200, 6300, 7800, 10400, 12500,
# 15600, 20800, 25000, ...
# other values will be rounded up to the next valid value
# rs92.alt2d: default altitude used by RS92 decoder if only 3 sats available
#-------------------------------#
rs41.agcbw=12500
rs41.rxbw=6300
rs92.rxbw=12500
rs92.alt2d=480
dfm.agcbw=20800
dfm.rxbw=10400
#-------------------------------#
# axudp for sending to aprsmap
#-------------------------------#
# local use only, do not feed to public services
# data not sanitized / quality checked, outliers not filtered out
axudp.active=1
axudp.host=monurl.fr
axudp.port=14580
axudp.symbol=/O
axudp.highrate=1
axudp.idformat=0
#-------------------------------#
# maybe some time in the future
#-------------------------------#
# currently simply not implemented, no need to put anything here anyway
tcp.active=0
tcp.host=radiosondy.info
tcp.port=14590
tcp.symbol=/O
tcp.highrate=20
tcp.idformat=0
#-------------------------------#
# EOF
#-------------------------------#

Binary file not shown.

Binary file not shown.

187
RadioSonde_FSK/data/index.html Executable file
View File

@ -0,0 +1,187 @@
<!DOCTYPE html>
<html>
<head>
<title>RadioSonde (version %VERSION_ID%)</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="data:,">
<link rel="stylesheet" type="text/css" href="style.css">
</head>
<body style="background-color: #008CBA;">
<div style="background-color: #FFFFFF;">
<h2><i onclick="w3_open()" class="fa fa-bars"></i>&nbsp;&nbsp;RadioSonde (version %VERSION_ID%)</h2>
</div>
<!--
<p>GPIO state: <strong> %STATE%</strong></p>
<p><a href="/on"><button class="button">ON</button></a></p>
<p><a href="/off"><button class="button button2">OFF</button></a></p>
-->
<!-- Side Navigation -->
<nav style="text-align: left; background-color: #aaa; font-weight: bold; display:none" id="mySidebar">
<h3>MENU</h3>
<button class="tablinks fa fa-close" onclick="w3_close();"> Close</button><br>
<button class="tablinks fa fa-dashboard" onclick="selTab(event,'QRG'); w3_close()" id="defaultTab"> QRG</button><br>
<button class="tablinks fa fa-wifi" onclick="selTab(event,'WiFi'); w3_close()"> WiFi</button><br>
<button class="tablinks fa fa-database" onclick="selTab(event,'Data'); w3_close()"> Data</button><br>
<button class="tablinks fa fa-map-marker" onclick="selTab(event,'SondeMap'); w3_close()"> URL SondeMap</button><br>
<button class="tablinks fa fa-gears" onclick="selTab(event,'Config'); w3_close()"> Config</button><br>
<button class="tablinks fa fa-sliders" onclick="selTab(event,'Control'); w3_close()"> Control</button><br>
<button class="tablinks fa fa-download" onclick="selTab(event,'Update'); w3_close()"> Update</button><br>
<button class="tablinks fa fa-support" onclick="selTab(event,'About'); w3_close()"> About</button><br><br><br><br>
</nav>
<div id="QRG" class="tabcontent">
<h3> QRG - Setup</h3>
<iframe src="qrg.html" style="border:none;" width="100%%" height="100%%"></iframe>
</div>
<div id="WiFi" class="tabcontent">
<h3> WiFi - Settings</h3>
<iframe src="wifi.html" style="border:none;" width="100%%" height="100%%"></iframe>
</div>
<div id="Data" class="tabcontent" data-src="status.html">
<h3>Data</h3>
<iframe src="status.html" style="border:none;" width="100%%" height="100%%"></iframe>
</div>
<div id="SondeMap" class="tabcontent" data-src="sondemap.html">
<iframe src="sondemap.html" style="border:none;" width="98%%" height="98%%"></iframe>
</div>
<!--
<div id="SondeMap" class="tabcontent" data-src="http://wx.dl2mf.de/#?">
<iframe src="http://wx.dl2mf.de/" style="border:none;" width="98%%" height="98%%"></iframe>
</div>
<div id="SondeMap2" class="tabcontent" data-src="https://radiosondy.info/">
<iframe src="https://radiosondy.info/" style="border:none;" width="98%%" height="98%%"></iframe>
</div>
<div id="SondeMap3" class="tabcontent" data-src="https://tracker.sondehub.org/?sondehub=1#!mt=osm&mz=8&qm=6_hours&mc=43.76665,6.24011&f=none&q=RS_*;*chase">
<iframe src="https://tracker.sondehub.org/?sondehub=1#!mt=osm&mz=8&qm=6_hours&mc=43.76665,6.24011&f=none&q=RS_*;*chase" style="border:none;" width="98%%" height="98%%"></iframe>
</div>
-->
<div id="Config" class="tabcontent">
<h3>Configuration</h3>
<iframe src="config.html" style="border:none;" width="100%%" height="100%%"></iframe>
</div>
<div id="Control" class="tabcontent">
<h3>Control</h3>
<iframe src="control.html" style="border:none;" width="100%%" height="100%%"></iframe>
</div>
<div id="Update" class="tabcontent">
<h3>Update</h3>
<iframe src="update.html" style="border:none;" width="100%%" height="100%%"></iframe>
</div>
<div id="About" class="tabcontent">
<h3>About</h3>
%VERSION_NAME%<br>
Copyright &copy; 2019 by Hansi Reiser, DL9RDZ<br>
(version %VERSION_ID%)<br>
with mods by <a href="https://www.dl2mf.de/" target="_blank">Meinhard Guenther, DL2MF</a><br>
<br>
Autodetect info: %AUTODETECT_INFO%<br>
<br>
CopyLeft 2020 Modifier par <a href="http://openpmr.cla.fr">FRS2013</a><br>
Licence GNU GPL <a href="https://www.gnu.org/licenses/gpl-2.0.txt">https://www.gnu.org/licenses/gpl-2.0.txt</a>
for details
</div>
<div style="background-color: #008CBA;"><br>
<h2>by FRS2013</h2>
</div>
<script>
function selTab(evt, id) {
var i, tabcontent, tablinks;
tabcontent=document.getElementsByClassName("tabcontent");
for(i=0; i<tabcontent.length; i++) {
tabcontent[i].style.display = "none";
var link = tabcontent[i].dataset.src;
if(link) {
var iframe = tabcontent[i].getElementsByTagName("iframe")[0];
iframe.setAttribute("src", "");
}
}
tablinks=document.getElementsByClassName("tablinks");
for(i=0; i<tablinks.length; i++) {
tablinks[i].className = tablinks[i].className.replace(" active", "");
}
var act = document.getElementById(id);
act.style.display = "block";
evt.currentTarget.className += " active";
var link = act.dataset.src;
if(link) {
var iframe = act.getElementsByTagName("iframe")[0];
iframe.setAttribute("src", link);
}
}
document.getElementById("defaultTab").click();
</script>
<!-- Script for Sidebar, Tabs, Accordions, Progress bars and slideshows -->
<script>
// Side navigation
function w3_open() {
var x = document.getElementById("mySidebar");
x.style.width = "100%";
x.style.height = "80%";
x.style.fontSize = "40px";
x.style.paddingTop = "0%";
x.style.display = "block";
}
function w3_close() {
document.getElementById("mySidebar").style.display = "none";
}
// Tabs
function openCity(evt, cityName) {
var i;
var x = document.getElementsByClassName("city");
for (i = 0; i < x.length; i++) {
x[i].style.display = "none";
}
var activebtn = document.getElementsByClassName("testbtn");
for (i = 0; i < x.length; i++) {
activebtn[i].className = activebtn[i].className.replace(" w3-dark-grey", "");
}
document.getElementById(cityName).style.display = "block";
evt.currentTarget.className += " w3-dark-grey";
}
var mybtn = document.getElementsByClassName("testbtn")[0];
mybtn.click();
// Accordions
function myAccFunc(id) {
var x = document.getElementById(id);
if (x.className.indexOf("w3-show") == -1) {
x.className += " w3-show";
} else {
x.className = x.className.replace(" w3-show", "");
}
}
// Slideshows
var slideIndex = 1;
function plusDivs(n) {
slideIndex = slideIndex + n;
showDivs(slideIndex);
}
function showDivs(n) {
var x = document.getElementsByClassName("mySlides");
if (n > x.length) {slideIndex = 1}
if (n < 1) {slideIndex = x.length} ;
for (i = 0; i < x.length; i++) {
x[i].style.display = "none";
}
x[slideIndex-1].style.display = "block";
}
showDivs(1);
</script>
</body>
</html>

View File

@ -0,0 +1,4 @@
Radiosonde
Radiosonde
VotreSSID
votrepass

24
RadioSonde_FSK/data/qrg.txt Executable file
View File

@ -0,0 +1,24 @@
# Frequency in Mhz (format nnn.nnn)
# Type (4=RS41, R=RS92, 6=DFM normal, DFM-06, 9=DFM inverted, DFM-09, M=M10)
#
400.000 9 + Test(FR)
401.500 4 - Santander(ES)
402.000 M + Nimes(FR)
402.800 4 + Cuneo(IT)
403.000 6 + Ajactio(FR)
403.010 6 + Canjuers(FR)
404.200 4 + Rome(IT)
404.500 4 - Payern(CH)
404.500 M - Bourges(FR)
404.789 9 + Frejus1(FR)
404.800 4 + Milan(IT)
405.000 R + Frejus2(FR)
401.199 M - Trappes(FR)
405.789 9 + Pegomas(FR)
405.100 4 - Lindenberg
405.700 4 - Bergen
405.900 4 - Bergen_2
405.100 4 - Meppen_2
405.300 4 - Essen
405.500 4 - Essen_2
# end

249
RadioSonde_FSK/data/screens.txt Executable file
View File

@ -0,0 +1,249 @@
# Definition of display content and action behaviour
#
# Timer: (view timer, rx timer, norx timer)
# - value -1: timer is disabled; value>=0: timer fires after (value) seconds
# - view timer: time since current view (display mode and sonde) was started
# - rx timer: time since when sonde data has been received continuously (trigger immediatly after RX)
# - norx timer: time since when no sonde data has been received continuously
# (rx and norx timer is started after tuning a new frequency and receiving a signal or not receiving
# anything for a 1s period)
#
# Actions:
# - W: activate WiFi scan
# - F: activate frequency spectrum display
# - 0: activate "Scan:" display (this is basically just display mode 0)
# - x: (1..N): activate display mode x [deprecated]
# - >: activate next display mode
# - D: activate default receiver display (display mode specified in config)
# - +: advance to next active sonde from QRG config
# - #: no action
#
# Display content (lower/upper case: small/large font)
# line,column=content
# for ILI9225 its also possible to indicate
# line,column,width=content for text within a box of width 'width'
# line,column,-width=content for right-justified text
#
# XText : Text
# F(suffix): frequency (with suffix, e.g., " MHz")
# L latitade
# O lOngitute
# A altitude
# Hm(suffix) hor. speed m/s (suffix: e.g. "m/s"; no suffix=>m/s as 16x8 bitmap for SSD1306 display only)
# Hk(suffix) hor. speed km/h (suffix: e.g. "km/h"; no suffix=>km/h as 16x8 bitmap for SSD1306 display only)
# V(suffix) vert. speef (suffix: e.g. "m/s"; no suffix=>m/s as 16x8 bitmap for SSD1306 display only)
# Ix sonde ID (dfm format by x: d=>dxlaprs, a=>autorx, s=>real serial number)
# Q signal quality statistics bar
# T type string (RS41/DFM9/DFM6/RS92)
# C afC value
# N ip address (only tiny font)
# S scan list entry info: l/empty: launch site name, #=entry nr, t=total entries, a=active entries, /: #/t
# K RS41 kill timer values: Kl launch timer, Kb burst timer, Kc kill countdown
# format: K_4: h:mm k_6: h:mm:ss k_s: sssss, nothing shown for other sonde
# Mx telemetry value x (t temp p preassure h hyg) [not yet implemented, maybe some day in future]
# Gx GPS-related data
# raw data from GPS: GA, GO, GH, GC: LAtitude, lOngitude, Altutide(Height), Course over ground
# relative to sonde: GD, GI, GB: Distance, dIrection (absolute), relative Bearing
# G0 GPS circle diagram e.g. 3,5=g0NCS,50,ff0000,000033,5,ffff00,4,ffffff
# "N" (what is on top: N=north C=course)
# "C" (where does the arrow point to: C=course, S=sonde)
# "S" (what is shown by the bullet: C=course, S=sonde)
# 50: circle radius, followed by fg and bg color
# 5: bullet radius, followed by fg color
# 4: arrow width, followed by fg color
# R RSSI
# B battery(T-Beam 1.0) S=status V=Batt.Volt C=charge current D=discharge current
# U=USB volt I=USB current T=IC temp
# AA Azimut degres
#
# fonts=x,y can be used to select font (x=small, y=large) for all items below
# for SSD1306, x and y can be used to select one of those fonts:
# (y should be a 1x2 font (1,5,6,7), x a small font)
# u8x8_font_chroma48medium8_r, // 0 ** default small
# u8x8_font_7x14_1x2_f, // 1 ** default large
# u8x8_font_amstrad_cpc_extended_f, // 2
# u8x8_font_5x7_f, // 3
# u8x8_font_5x8_f, // 4
# u8x8_font_8x13_1x2_f, // 5
# u8x8_font_8x13B_1x2_f, // 6
# u8x8_font_7x14B_1x2_f, // 7
# u8x8_font_artossans8_r, // 8
# u8x8_font_artosserif8_r, // 9
# u8x8_font_torussansbold8_r, // 10
# u8x8_font_victoriabold8_r, // 11
# u8x8_font_victoriamedium8_r, // 12
# u8x8_font_pressstart2p_f, // 13
# u8x8_font_pcsenior_f, // 14
# u8x8_font_pxplusibmcgathin_f, // 15
# u8x8_font_pxplusibmcga_f, // 16
# u8x8_font_pxplustandynewtv_f, // 17
#
#
# color=rrggbb,rrggbb can be used to select color (foreground, background)
# see https://github.com/Nkawu/TFT_22_ILI9225/wiki#color-reference for example (use without "#"-sign)
#
# for TFT display, coordinates and width are multiplied by xscale,yscale and later used in pixels
# with scale=1,1 you can directly use pixel coordinates. (default: xscale=13,yscale=22 => 8 lines, 16 columns)
###########
#
# Default configuration for "Scanner" display:
# - view timer disabled; rx timer=0; norx timer = 0
# => after 1 second immediately an action is triggered
# (norx: go to next sonde; rx: go to default receiver display)
# - key1 actions: D,0,F,W
# => Button press activates default receiver view, double press does nothing
# Mid press activates Spectrum display, long press activates Wifi scan
# - key2 has no function
@Scanner
timer=-1,0,0
key1action=D,#,F,W
key2action=#,#,#,#
timeaction=#,D,+
0,0=XScan
0,5=S#:
0,9=T
3,0=F MHz
5,0=S
7,0=y%
7,5=n
############
# Default configuration for "Legacy" display:
# - view timer=-1, rx timer=-1 (disabled); norx timer=20 (or -1 for "old" behaviour)
# => norx timer fires after not receiving a singla for 20 seconds
# - key1 actions: +,0,F,W
# => Button1 press: next sonde; double press => @Scanner display
# => Mid press activates Spectrum display, long press activates Wifi scan
# - key2 actions: 2,#,#,#
# => BUtton2 activates display 2 (@Field)
# - timer actions: #,#,0
# (norx timer: if no signal for >20 seconds: go back to scanner mode)
#
@Legacy
timer=-1,-1,N
key1action=+,0,F,W
key2action=>,#,#,#
timeaction=#,#,0
0,0=t
0,5=f MHz
1,0=is
1,8=c
2,0=L
2,10=a
3,10=h
4,0=O
4,9=v
5,9=gC
5,13=gB
6,0=R
6,7=Q
7,6=gD
7,12=gI°
############
# Configuratoon for "Field" display (display 2)
# similar to @Legacy, but no norx timer, and Key2 goes to display 4
@Field
timer=-1,-1,N
key1action=+,0,F,W
key2action=>,#,#,#
timeaction=#,#,#
0,0=Is
2,0=L
2,9=xEl:
2,12=gE
4,0=O
3,10=h
4,9=v
5,9=gC
5,13=gB
6,0=A
6,7=Q
7,6=gD
7,12=gI°
############
# Configuration for "Field2" display (display 3)
# similar to @Field
@Field2
timer=-1,-1,N
key1action=+,0,F,W
key2action=>,#,#,#
timeaction=#,#,#
0,0=Is
0,9=f
1,12=t
2,9=xEl:
2,12=gE
2,0=L
3,10=h
4,0=O
4,9=v
5,9=gC
5,13=gB
6,0=A
6,7=Q
7,6=gD
7,12=gI°
#############
# Configuration for "GPS" display
# not yet the final version, just for testing
@GPSDIST
timer=-1,-1,-1
key1action=+,0,F,W
key2action=>,#,#,#
timeaction=#,#,#
0,0=Is
0,9=f
1,12=t
2,0=L
4,0=O
2,10=a
3,10=h
4,9=v
5,9=gC
5,13=gB
6,0=xEl:
6,2=gE
6,7=Q
7,0=gW
7,2=xd=
7,4=gD
7,11=gI°
############
# Boussole Oled
@BOUSSOLE
timer=-1,-1,-1
key1action=+,0,F,W
key2action=>,#,#,#
timeaction=#,#,#
0,0=g0
0,10=a
1,10=h
2,9=v
3,10=gC
3,13=gB
4,9=gD
5,11=gI°
6,9=xEl:
6,11=gE
7,9=t
############
@BatteryOLED
timer=-1,-1,-1
key1action=+,0,F,W
key2action=>,#,#,#
timeaction=#,#,#
fonts=0,1
0,0=xBat.Status:
2,0=xCpu:
2,7=bVV
4,0=xVbus:
4,7=bCV
6,0=xPower:
6,7=bPmW

View File

@ -0,0 +1,10 @@
<html>
<head>
<link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\">
</head>
<body>
<button class="tablinks" onclick="javascript:window.open('http://wx.dl2mf.de/','_self')"> Sonde Map</button><br><br>
<button class="tablinks" onclick="javascript:window.open('https://radiosondy.info/','_self')"> RadioSony</button><br><br>
<button class="tablinks" onclick="javascript:window.open('https://tracker.sondehub.org/?sondehub=1#!mt=osm&mz=8&qm=6_hours&mc=43.76665,6.24011&f=none&q=RS_*;*chase','_self')"> SondeHub</button><br><br>
</body>
</html>

659
RadioSonde_FSK/data/style.css Executable file
View File

@ -0,0 +1,659 @@
body, html {
height: 100%;
margin: 0;
font-family: Arial;
}
table, th, td {
border: 1px solid black;
border-collapse: collapse;
background-color: #ddd
}
td#caption {
text-align: center;
background-color: #aaa;
font-weight: bold;
}
td#sfreq {
background-color: #ccc;
}
.tab {
overflow: hidden;
border: 1px solid #ccc;
}
.tab button {
background-color: inherit;
float: left;
border: none;
outline: none;
cursor: pointer;
padding: 14px 16px;
transition: 0.3s;
}
.tab button:hover {
background-color: #ddd;
}
.tab button.active {
background-color: #ccc;
}
.tabcontent {
display: none;
padding: 6px 12px;
border: 1px solid #ccc;
border-top: none;
height: 100%;
background-color: #FFFFFF;
}
html {
font-family: Helvetica;
display: inline-block;
margin: 0px auto;
text-align: center;
}
h1{
color: #0F3376;
padding: 2vh;
}
p{
font-size: 1.5rem;
}
.button {
display: inline-block;
background-color: #008CBA;
border: none;
border-radius: 4px;
color: white;
padding: 16px 40px;
text-decoration: none;
font-size: 30px;
margin: 2px;
cursor: pointer;
}
.button2 {
background-color: #f44336;
}
/*!
* Font Awesome 4.3.0 by @davegandy - http://fontawesome.io - @fontawesome
* License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)
*/@font-face{font-family:'FontAwesome';
src:url('fontawesome-webfont.eot?v=4.3.0');
src:url('fontawesome-webfont.eot?#iefix&v=4.3.0') format('embedded-opentype'),url('fontawesome-webfont.woff2?v=4.3.0') format('woff2'),url('fontawesome-webfont.woff?v=4.3.0') format('woff'),url('fontawesome-webfont.ttf?v=4.3.0') format('truetype'),url('fontawesome-webfont.svg?v=4.3.0#fontawesomeregular') format('svg');
font-weight:normal;
font-style:normal}.fa{display:inline-block;
font:normal normal normal 14px/1 FontAwesome;
font-size:inherit;
text-rendering:auto;
-webkit-font-smoothing:antialiased;
-moz-osx-font-smoothing:grayscale;
transform:translate(0, 0)}.fa-lg{font-size:1.33333333em;
line-height:.75em;
vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;
text-align:center}.fa-ul{padding-left:0;
margin-left:2.14285714em;
list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;
left:-2.14285714em;
width:2.14285714em;
top:.14285714em;
text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;
border:solid .08em #eee;
border-radius:.1em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;
animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);
animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);
transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);
transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);
transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);
transform:rotate(359deg)}}.fa-rotate-90{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1);
-webkit-transform:rotate(90deg);
-ms-transform:rotate(90deg);
transform:rotate(90deg)}.fa-rotate-180{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2);
-webkit-transform:rotate(180deg);
-ms-transform:rotate(180deg);
transform:rotate(180deg)}.fa-rotate-270{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=3);
-webkit-transform:rotate(270deg);
-ms-transform:rotate(270deg);
transform:rotate(270deg)}.fa-flip-horizontal{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1);
-webkit-transform:scale(-1, 1);
-ms-transform:scale(-1, 1);
transform:scale(-1, 1)}.fa-flip-vertical{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1);
-webkit-transform:scale(1, -1);
-ms-transform:scale(1, -1);
transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;
display:inline-block;
width:2em;
height:2em;
line-height:2em;
vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;
left:0;
width:100%;
text-align:center}
.fa-stack-1x{line-height:inherit}
.fa-stack-2x{font-size:2em}
.fa-inverse{color:#fff}
.fa-glass:before{content:"\f000"}
.fa-music:before{content:"\f001"}
.fa-search:before{content:"\f002"}
.fa-envelope-o:before{content:"\f003"}
.fa-heart:before{content:"\f004"}
.fa-star:before{content:"\f005"}
.fa-star-o:before{content:"\f006"}
.fa-user:before{content:"\f007"}
.fa-film:before{content:"\f008"}
.fa-th-large:before{content:"\f009"}
.fa-th:before{content:"\f00a"}
.fa-th-list:before{content:"\f00b"}
.fa-check:before{content:"\f00c"}
.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}
.fa-search-plus:before{content:"\f00e"}
.fa-search-minus:before{content:"\f010"}
.fa-power-off:before{content:"\f011"}
.fa-signal:before{content:"\f012"}
.fa-gear:before,.fa-cog:before{content:"\f013"}
.fa-trash-o:before{content:"\f014"}
.fa-home:before{content:"\f015"}
.fa-file-o:before{content:"\f016"}
.fa-clock-o:before{content:"\f017"}
.fa-road:before{content:"\f018"}
.fa-download:before{content:"\f019"}
.fa-arrow-circle-o-down:before{content:"\f01a"}
.fa-arrow-circle-o-up:before{content:"\f01b"}
.fa-inbox:before{content:"\f01c"}
.fa-play-circle-o:before{content:"\f01d"}
.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}
.fa-refresh:before{content:"\f021"}
.fa-list-alt:before{content:"\f022"}
.fa-lock:before{content:"\f023"}
.fa-flag:before{content:"\f024"}
.fa-headphones:before{content:"\f025"}
.fa-volume-off:before{content:"\f026"}
.fa-volume-down:before{content:"\f027"}
.fa-volume-up:before{content:"\f028"}
.fa-qrcode:before{content:"\f029"}
.fa-barcode:before{content:"\f02a"}
.fa-tag:before{content:"\f02b"}
.fa-tags:before{content:"\f02c"}
.fa-book:before{content:"\f02d"}
.fa-bookmark:before{content:"\f02e"}
.fa-print:before{content:"\f02f"}
.fa-camera:before{content:"\f030"}
.fa-font:before{content:"\f031"}
.fa-bold:before{content:"\f032"}
.fa-italic:before{content:"\f033"}
.fa-text-height:before{content:"\f034"}
.fa-text-width:before{content:"\f035"}
.fa-align-left:before{content:"\f036"}
.fa-align-center:before{content:"\f037"}
.fa-align-right:before{content:"\f038"}
.fa-align-justify:before{content:"\f039"}
.fa-list:before{content:"\f03a"}
.fa-dedent:before,.fa-outdent:before{content:"\f03b"}
.fa-indent:before{content:"\f03c"}
.fa-video-camera:before{content:"\f03d"}
.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}
.fa-pencil:before{content:"\f040"}
.fa-map-marker:before{content:"\f041"}
.fa-adjust:before{content:"\f042"}
.fa-tint:before{content:"\f043"}
.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}
.fa-share-square-o:before{content:"\f045"}
.fa-check-square-o:before{content:"\f046"}
.fa-arrows:before{content:"\f047"}
.fa-step-backward:before{content:"\f048"}
.fa-fast-backward:before{content:"\f049"}
.fa-backward:before{content:"\f04a"}
.fa-play:before{content:"\f04b"}
.fa-pause:before{content:"\f04c"}
.fa-stop:before{content:"\f04d"}
.fa-forward:before{content:"\f04e"}
.fa-fast-forward:before{content:"\f050"}
.fa-step-forward:before{content:"\f051"}
.fa-eject:before{content:"\f052"}
.fa-chevron-left:before{content:"\f053"}
.fa-chevron-right:before{content:"\f054"}
.fa-plus-circle:before{content:"\f055"}
.fa-minus-circle:before{content:"\f056"}
.fa-times-circle:before{content:"\f057"}
.fa-check-circle:before{content:"\f058"}
.fa-question-circle:before{content:"\f059"}
.fa-info-circle:before{content:"\f05a"}
.fa-crosshairs:before{content:"\f05b"}
.fa-times-circle-o:before{content:"\f05c"}
.fa-check-circle-o:before{content:"\f05d"}
.fa-ban:before{content:"\f05e"}
.fa-arrow-left:before{content:"\f060"}
.fa-arrow-right:before{content:"\f061"}
.fa-arrow-up:before{content:"\f062"}
.fa-arrow-down:before{content:"\f063"}
.fa-mail-forward:before,.fa-share:before{content:"\f064"}
.fa-expand:before{content:"\f065"}
.fa-compress:before{content:"\f066"}
.fa-plus:before{content:"\f067"}
.fa-minus:before{content:"\f068"}
.fa-asterisk:before{content:"\f069"}
.fa-exclamation-circle:before{content:"\f06a"}
.fa-gift:before{content:"\f06b"}
.fa-leaf:before{content:"\f06c"}
.fa-fire:before{content:"\f06d"}
.fa-eye:before{content:"\f06e"}
.fa-eye-slash:before{content:"\f070"}
.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}
.fa-plane:before{content:"\f072"}
.fa-calendar:before{content:"\f073"}
.fa-random:before{content:"\f074"}
.fa-comment:before{content:"\f075"}
.fa-magnet:before{content:"\f076"}
.fa-chevron-up:before{content:"\f077"}
.fa-chevron-down:before{content:"\f078"}
.fa-retweet:before{content:"\f079"}
.fa-shopping-cart:before{content:"\f07a"}
.fa-folder:before{content:"\f07b"}
.fa-folder-open:before{content:"\f07c"}
.fa-arrows-v:before{content:"\f07d"}
.fa-arrows-h:before{content:"\f07e"}
.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}
.fa-twitter-square:before{content:"\f081"}
.fa-facebook-square:before{content:"\f082"}
.fa-camera-retro:before{content:"\f083"}
.fa-key:before{content:"\f084"}
.fa-gears:before,.fa-cogs:before{content:"\f085"}
.fa-comments:before{content:"\f086"}
.fa-thumbs-o-up:before{content:"\f087"}
.fa-thumbs-o-down:before{content:"\f088"}
.fa-star-half:before{content:"\f089"}
.fa-heart-o:before{content:"\f08a"}
.fa-sign-out:before{content:"\f08b"}
.fa-linkedin-square:before{content:"\f08c"}
.fa-thumb-tack:before{content:"\f08d"}
.fa-external-link:before{content:"\f08e"}
.fa-sign-in:before{content:"\f090"}
.fa-trophy:before{content:"\f091"}
.fa-github-square:before{content:"\f092"}
.fa-upload:before{content:"\f093"}
.fa-lemon-o:before{content:"\f094"}
.fa-phone:before{content:"\f095"}
.fa-square-o:before{content:"\f096"}
.fa-bookmark-o:before{content:"\f097"}
.fa-phone-square:before{content:"\f098"}
.fa-twitter:before{content:"\f099"}
.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}
.fa-github:before{content:"\f09b"}
.fa-unlock:before{content:"\f09c"}
.fa-credit-card:before{content:"\f09d"}
.fa-rss:before{content:"\f09e"}
.fa-hdd-o:before{content:"\f0a0"}
.fa-bullhorn:before{content:"\f0a1"}
.fa-bell:before{content:"\f0f3"}
.fa-certificate:before{content:"\f0a3"}
.fa-hand-o-right:before{content:"\f0a4"}
.fa-hand-o-left:before{content:"\f0a5"}
.fa-hand-o-up:before{content:"\f0a6"}
.fa-hand-o-down:before{content:"\f0a7"}
.fa-arrow-circle-left:before{content:"\f0a8"}
.fa-arrow-circle-right:before{content:"\f0a9"}
.fa-arrow-circle-up:before{content:"\f0aa"}
.fa-arrow-circle-down:before{content:"\f0ab"}
.fa-globe:before{content:"\f0ac"}
.fa-wrench:before{content:"\f0ad"}
.fa-tasks:before{content:"\f0ae"}
.fa-filter:before{content:"\f0b0"}
.fa-briefcase:before{content:"\f0b1"}
.fa-arrows-alt:before{content:"\f0b2"}
.fa-group:before,.fa-users:before{content:"\f0c0"}
.fa-chain:before,.fa-link:before{content:"\f0c1"}
.fa-cloud:before{content:"\f0c2"}
.fa-flask:before{content:"\f0c3"}
.fa-cut:before,.fa-scissors:before{content:"\f0c4"}
.fa-copy:before,.fa-files-o:before{content:"\f0c5"}
.fa-paperclip:before{content:"\f0c6"}
.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}
.fa-square:before{content:"\f0c8"}
.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}
.fa-list-ul:before{content:"\f0ca"}
.fa-list-ol:before{content:"\f0cb"}
.fa-strikethrough:before{content:"\f0cc"}
.fa-underline:before{content:"\f0cd"}
.fa-table:before{content:"\f0ce"}
.fa-magic:before{content:"\f0d0"}
.fa-truck:before{content:"\f0d1"}
.fa-pinterest:before{content:"\f0d2"}
.fa-pinterest-square:before{content:"\f0d3"}
.fa-google-plus-square:before{content:"\f0d4"}
.fa-google-plus:before{content:"\f0d5"}
.fa-money:before{content:"\f0d6"}
.fa-caret-down:before{content:"\f0d7"}
.fa-caret-up:before{content:"\f0d8"}
.fa-caret-left:before{content:"\f0d9"}
.fa-caret-right:before{content:"\f0da"}
.fa-columns:before{content:"\f0db"}
.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}
.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}
.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}
.fa-envelope:before{content:"\f0e0"}
.fa-linkedin:before{content:"\f0e1"}
.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}
.fa-legal:before,.fa-gavel:before{content:"\f0e3"}
.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}
.fa-comment-o:before{content:"\f0e5"}
.fa-comments-o:before{content:"\f0e6"}
.fa-flash:before,.fa-bolt:before{content:"\f0e7"}
.fa-sitemap:before{content:"\f0e8"}
.fa-umbrella:before{content:"\f0e9"}
.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}
.fa-lightbulb-o:before{content:"\f0eb"}
.fa-exchange:before{content:"\f0ec"}
.fa-cloud-download:before{content:"\f0ed"}
.fa-cloud-upload:before{content:"\f0ee"}
.fa-user-md:before{content:"\f0f0"}
.fa-stethoscope:before{content:"\f0f1"}
.fa-suitcase:before{content:"\f0f2"}
.fa-bell-o:before{content:"\f0a2"}
.fa-coffee:before{content:"\f0f4"}
.fa-cutlery:before{content:"\f0f5"}
.fa-file-text-o:before{content:"\f0f6"}
.fa-building-o:before{content:"\f0f7"}
.fa-hospital-o:before{content:"\f0f8"}
.fa-ambulance:before{content:"\f0f9"}
.fa-medkit:before{content:"\f0fa"}
.fa-fighter-jet:before{content:"\f0fb"}
.fa-beer:before{content:"\f0fc"}
.fa-h-square:before{content:"\f0fd"}
.fa-plus-square:before{content:"\f0fe"}
.fa-angle-double-left:before{content:"\f100"}
.fa-angle-double-right:before{content:"\f101"}
.fa-angle-double-up:before{content:"\f102"}
.fa-angle-double-down:before{content:"\f103"}
.fa-angle-left:before{content:"\f104"}
.fa-angle-right:before{content:"\f105"}
.fa-angle-up:before{content:"\f106"}
.fa-angle-down:before{content:"\f107"}
.fa-desktop:before{content:"\f108"}
.fa-laptop:before{content:"\f109"}
.fa-tablet:before{content:"\f10a"}
.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}
.fa-circle-o:before{content:"\f10c"}
.fa-quote-left:before{content:"\f10d"}
.fa-quote-right:before{content:"\f10e"}
.fa-spinner:before{content:"\f110"}
.fa-circle:before{content:"\f111"}
.fa-mail-reply:before,.fa-reply:before{content:"\f112"}
.fa-github-alt:before{content:"\f113"}
.fa-folder-o:before{content:"\f114"}
.fa-folder-open-o:before{content:"\f115"}
.fa-smile-o:before{content:"\f118"}
.fa-frown-o:before{content:"\f119"}
.fa-meh-o:before{content:"\f11a"}
.fa-gamepad:before{content:"\f11b"}
.fa-keyboard-o:before{content:"\f11c"}
.fa-flag-o:before{content:"\f11d"}
.fa-flag-checkered:before{content:"\f11e"}
.fa-terminal:before{content:"\f120"}
.fa-code:before{content:"\f121"}
.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}
.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}
.fa-location-arrow:before{content:"\f124"}
.fa-crop:before{content:"\f125"}
.fa-code-fork:before{content:"\f126"}
.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}
.fa-question:before{content:"\f128"}
.fa-info:before{content:"\f129"}
.fa-exclamation:before{content:"\f12a"}
.fa-superscript:before{content:"\f12b"}
.fa-subscript:before{content:"\f12c"}
.fa-eraser:before{content:"\f12d"}
.fa-puzzle-piece:before{content:"\f12e"}
.fa-microphone:before{content:"\f130"}
.fa-microphone-slash:before{content:"\f131"}
.fa-shield:before{content:"\f132"}
.fa-calendar-o:before{content:"\f133"}
.fa-fire-extinguisher:before{content:"\f134"}
.fa-rocket:before{content:"\f135"}
.fa-maxcdn:before{content:"\f136"}
.fa-chevron-circle-left:before{content:"\f137"}
.fa-chevron-circle-right:before{content:"\f138"}
.fa-chevron-circle-up:before{content:"\f139"}
.fa-chevron-circle-down:before{content:"\f13a"}
.fa-html5:before{content:"\f13b"}
.fa-css3:before{content:"\f13c"}
.fa-anchor:before{content:"\f13d"}
.fa-unlock-alt:before{content:"\f13e"}
.fa-bullseye:before{content:"\f140"}
.fa-ellipsis-h:before{content:"\f141"}
.fa-ellipsis-v:before{content:"\f142"}
.fa-rss-square:before{content:"\f143"}
.fa-play-circle:before{content:"\f144"}
.fa-ticket:before{content:"\f145"}
.fa-minus-square:before{content:"\f146"}
.fa-minus-square-o:before{content:"\f147"}
.fa-level-up:before{content:"\f148"}
.fa-level-down:before{content:"\f149"}
.fa-check-square:before{content:"\f14a"}
.fa-pencil-square:before{content:"\f14b"}
.fa-external-link-square:before{content:"\f14c"}
.fa-share-square:before{content:"\f14d"}
.fa-compass:before{content:"\f14e"}
.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}
.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}
.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}
.fa-euro:before,.fa-eur:before{content:"\f153"}
.fa-gbp:before{content:"\f154"}
.fa-dollar:before,.fa-usd:before{content:"\f155"}
.fa-rupee:before,.fa-inr:before{content:"\f156"}
.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}
.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}
.fa-won:before,.fa-krw:before{content:"\f159"}
.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}
.fa-file:before{content:"\f15b"}
.fa-file-text:before{content:"\f15c"}
.fa-sort-alpha-asc:before{content:"\f15d"}
.fa-sort-alpha-desc:before{content:"\f15e"}
.fa-sort-amount-asc:before{content:"\f160"}
.fa-sort-amount-desc:before{content:"\f161"}
.fa-sort-numeric-asc:before{content:"\f162"}
.fa-sort-numeric-desc:before{content:"\f163"}
.fa-thumbs-up:before{content:"\f164"}
.fa-thumbs-down:before{content:"\f165"}
.fa-youtube-square:before{content:"\f166"}
.fa-youtube:before{content:"\f167"}
.fa-xing:before{content:"\f168"}
.fa-xing-square:before{content:"\f169"}
.fa-youtube-play:before{content:"\f16a"}
.fa-dropbox:before{content:"\f16b"}
.fa-stack-overflow:before{content:"\f16c"}
.fa-instagram:before{content:"\f16d"}
.fa-flickr:before{content:"\f16e"}
.fa-adn:before{content:"\f170"}
.fa-bitbucket:before{content:"\f171"}
.fa-bitbucket-square:before{content:"\f172"}
.fa-tumblr:before{content:"\f173"}
.fa-tumblr-square:before{content:"\f174"}
.fa-long-arrow-down:before{content:"\f175"}
.fa-long-arrow-up:before{content:"\f176"}
.fa-long-arrow-left:before{content:"\f177"}
.fa-long-arrow-right:before{content:"\f178"}
.fa-apple:before{content:"\f179"}
.fa-windows:before{content:"\f17a"}
.fa-android:before{content:"\f17b"}
.fa-linux:before{content:"\f17c"}
.fa-dribbble:before{content:"\f17d"}
.fa-skype:before{content:"\f17e"}
.fa-foursquare:before{content:"\f180"}
.fa-trello:before{content:"\f181"}
.fa-female:before{content:"\f182"}
.fa-male:before{content:"\f183"}
.fa-gittip:before,.fa-gratipay:before{content:"\f184"}
.fa-sun-o:before{content:"\f185"}
.fa-moon-o:before{content:"\f186"}
.fa-archive:before{content:"\f187"}
.fa-bug:before{content:"\f188"}
.fa-vk:before{content:"\f189"}
.fa-weibo:before{content:"\f18a"}
.fa-renren:before{content:"\f18b"}
.fa-pagelines:before{content:"\f18c"}
.fa-stack-exchange:before{content:"\f18d"}
.fa-arrow-circle-o-right:before{content:"\f18e"}
.fa-arrow-circle-o-left:before{content:"\f190"}
.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}
.fa-dot-circle-o:before{content:"\f192"}
.fa-wheelchair:before{content:"\f193"}
.fa-vimeo-square:before{content:"\f194"}
.fa-turkish-lira:before,.fa-try:before{content:"\f195"}
.fa-plus-square-o:before{content:"\f196"}
.fa-space-shuttle:before{content:"\f197"}
.fa-slack:before{content:"\f198"}
.fa-envelope-square:before{content:"\f199"}
.fa-wordpress:before{content:"\f19a"}
.fa-openid:before{content:"\f19b"}
.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}
.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}
.fa-yahoo:before{content:"\f19e"}
.fa-google:before{content:"\f1a0"}
.fa-reddit:before{content:"\f1a1"}
.fa-reddit-square:before{content:"\f1a2"}
.fa-stumbleupon-circle:before{content:"\f1a3"}
.fa-stumbleupon:before{content:"\f1a4"}
.fa-delicious:before{content:"\f1a5"}
.fa-digg:before{content:"\f1a6"}
.fa-pied-piper:before{content:"\f1a7"}
.fa-pied-piper-alt:before{content:"\f1a8"}
.fa-drupal:before{content:"\f1a9"}
.fa-joomla:before{content:"\f1aa"}
.fa-language:before{content:"\f1ab"}
.fa-fax:before{content:"\f1ac"}
.fa-building:before{content:"\f1ad"}
.fa-child:before{content:"\f1ae"}
.fa-paw:before{content:"\f1b0"}
.fa-spoon:before{content:"\f1b1"}
.fa-cube:before{content:"\f1b2"}
.fa-cubes:before{content:"\f1b3"}
.fa-behance:before{content:"\f1b4"}
.fa-behance-square:before{content:"\f1b5"}
.fa-steam:before{content:"\f1b6"}
.fa-steam-square:before{content:"\f1b7"}
.fa-recycle:before{content:"\f1b8"}
.fa-automobile:before,.fa-car:before{content:"\f1b9"}
.fa-cab:before,.fa-taxi:before{content:"\f1ba"}
.fa-tree:before{content:"\f1bb"}
.fa-spotify:before{content:"\f1bc"}
.fa-deviantart:before{content:"\f1bd"}
.fa-soundcloud:before{content:"\f1be"}
.fa-database:before{content:"\f1c0"}
.fa-file-pdf-o:before{content:"\f1c1"}
.fa-file-word-o:before{content:"\f1c2"}
.fa-file-excel-o:before{content:"\f1c3"}
.fa-file-powerpoint-o:before{content:"\f1c4"}
.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}
.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}
.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}
.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}
.fa-file-code-o:before{content:"\f1c9"}
.fa-vine:before{content:"\f1ca"}
.fa-codepen:before{content:"\f1cb"}
.fa-jsfiddle:before{content:"\f1cc"}
.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}
.fa-circle-o-notch:before{content:"\f1ce"}
.fa-ra:before,.fa-rebel:before{content:"\f1d0"}
.fa-ge:before,.fa-empire:before{content:"\f1d1"}
.fa-git-square:before{content:"\f1d2"}
.fa-git:before{content:"\f1d3"}
.fa-hacker-news:before{content:"\f1d4"}
.fa-tencent-weibo:before{content:"\f1d5"}
.fa-qq:before{content:"\f1d6"}
.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}
.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}
.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}
.fa-history:before{content:"\f1da"}
.fa-genderless:before,.fa-circle-thin:before{content:"\f1db"}
.fa-header:before{content:"\f1dc"}
.fa-paragraph:before{content:"\f1dd"}
.fa-sliders:before{content:"\f1de"}
.fa-share-alt:before{content:"\f1e0"}
.fa-share-alt-square:before{content:"\f1e1"}
.fa-bomb:before{content:"\f1e2"}
.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}
.fa-tty:before{content:"\f1e4"}
.fa-binoculars:before{content:"\f1e5"}
.fa-plug:before{content:"\f1e6"}
.fa-slideshare:before{content:"\f1e7"}
.fa-twitch:before{content:"\f1e8"}
.fa-yelp:before{content:"\f1e9"}
.fa-newspaper-o:before{content:"\f1ea"}
.fa-wifi:before{content:"\f1eb"}
.fa-calculator:before{content:"\f1ec"}
.fa-paypal:before{content:"\f1ed"}
.fa-google-wallet:before{content:"\f1ee"}
.fa-cc-visa:before{content:"\f1f0"}
.fa-cc-mastercard:before{content:"\f1f1"}
.fa-cc-discover:before{content:"\f1f2"}
.fa-cc-amex:before{content:"\f1f3"}
.fa-cc-paypal:before{content:"\f1f4"}
.fa-cc-stripe:before{content:"\f1f5"}
.fa-bell-slash:before{content:"\f1f6"}
.fa-bell-slash-o:before{content:"\f1f7"}
.fa-trash:before{content:"\f1f8"}
.fa-copyright:before{content:"\f1f9"}
.fa-at:before{content:"\f1fa"}
.fa-eyedropper:before{content:"\f1fb"}
.fa-paint-brush:before{content:"\f1fc"}
.fa-birthday-cake:before{content:"\f1fd"}
.fa-area-chart:before{content:"\f1fe"}
.fa-pie-chart:before{content:"\f200"}
.fa-line-chart:before{content:"\f201"}
.fa-lastfm:before{content:"\f202"}
.fa-lastfm-square:before{content:"\f203"}
.fa-toggle-off:before{content:"\f204"}
.fa-toggle-on:before{content:"\f205"}
.fa-bicycle:before{content:"\f206"}
.fa-bus:before{content:"\f207"}
.fa-ioxhost:before{content:"\f208"}
.fa-angellist:before{content:"\f209"}
.fa-cc:before{content:"\f20a"}
.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}
.fa-meanpath:before{content:"\f20c"}
.fa-buysellads:before{content:"\f20d"}
.fa-connectdevelop:before{content:"\f20e"}
.fa-dashcube:before{content:"\f210"}
.fa-forumbee:before{content:"\f211"}
.fa-leanpub:before{content:"\f212"}
.fa-sellsy:before{content:"\f213"}
.fa-shirtsinbulk:before{content:"\f214"}
.fa-simplybuilt:before{content:"\f215"}
.fa-skyatlas:before{content:"\f216"}
.fa-cart-plus:before{content:"\f217"}
.fa-cart-arrow-down:before{content:"\f218"}
.fa-diamond:before{content:"\f219"}
.fa-ship:before{content:"\f21a"}
.fa-user-secret:before{content:"\f21b"}
.fa-motorcycle:before{content:"\f21c"}
.fa-street-view:before{content:"\f21d"}
.fa-heartbeat:before{content:"\f21e"}
.fa-venus:before{content:"\f221"}
.fa-mars:before{content:"\f222"}
.fa-mercury:before{content:"\f223"}
.fa-transgender:before{content:"\f224"}
.fa-transgender-alt:before{content:"\f225"}
.fa-venus-double:before{content:"\f226"}
.fa-mars-double:before{content:"\f227"}
.fa-venus-mars:before{content:"\f228"}
.fa-mars-stroke:before{content:"\f229"}
.fa-mars-stroke-v:before{content:"\f22a"}
.fa-mars-stroke-h:before{content:"\f22b"}
.fa-neuter:before{content:"\f22c"}
.fa-facebook-official:before{content:"\f230"}
.fa-pinterest-p:before{content:"\f231"}
.fa-whatsapp:before{content:"\f232"}
.fa-server:before{content:"\f233"}
.fa-user-plus:before{content:"\f234"}
.fa-user-times:before{content:"\f235"}
.fa-hotel:before,.fa-bed:before{content:"\f236"}
.fa-viacoin:before{content:"\f237"}
.fa-train:before{content:"\f238"}
.fa-subway:before{content:"\f239"}
.fa-medium:before{content:"\f23a"}

4
RadioSonde_FSK/version.h Executable file
View File

@ -0,0 +1,4 @@
const char *version_name = "RadioSonde";
const char *version_id = "0.8.5";
const int SPIFFS_MAJOR=2;
const int SPIFFS_MINOR=4;

101
Setup.md Executable file
View File

@ -0,0 +1,101 @@
# Près-requis
<img src="http://xavier.debert.free.fr/RS/TTGO2.jpg" width="50%">
## Arduino IDE
Avoir une version à jour exemple 1.8.13(juillet2020)
## ESP32 support
Fichier -> Préférences
en bas dans la case "URL de gestionnaire de cartes supplémentaires"
Ajouter *https://dl.espressif.com/dl/package_esp32_index.json* et appuier sur oK
Puis dans Outils
Outils -> type de cartes -> Gestionnaire de cartes
dans la case de recherche taper "esp32"
Installer "esp32 by Espressif Systems"
Puis après
## ESP32 Flash Filesystem Upload support
Télécharger le fichier zip de la dernière version
https://github.com/me-no-dev/arduino-esp32fs-plugin/releases/
Décompresser l'archive dans le répértoire tools de votre Arduino IDE
## Ajouter les bibliothèques
Séléctionner Outils -> Gestionnaire de bibliothèques
Installer "U8g2"
Installer "MicroNMEA"
Installer "TFT_22_ILI9225" nécessaire pas pour l'écran car j'ai tout supprimé
mais pour les fonts, car le TTGO fonctionne avec OLED SSD1306 par défaut!
## Ajouter les bibliothèques, parties 2
Depuis https://github.com/me-no-dev/ESPAsyncWebServer télécharger le ZIP , l'extraire dans "libraries"
, renommer le répertoire en ESPAsyncWebServer (supprimer juste "-master")
Depuis https://github.com/me-no-dev/AsyncTCP télécharger le ZIP, l'extraire dans "libraries", et renommer le répertoire en AsyncTCP
de même pour https://github.com/lewisxhe/AXP202X_Library télécharger le ZIP, l'extraire dans "libraries", et renommer le répertoire en AXP202X_Library
## Ajouter les bibliothèques, parties 3
Copier libraries/SX1278FSK
et libraries/SondeLib
et libraries/fonts
fourni dans libraries
```
ou sous Linux un lien symbolique est aussi possible mais pas obligatoire!
cd ~/Arduino/libraries
ln -s <whereyouclonedthegit>/radiosonde/libraries/SondeLib/ .
ln -s <whereyouclonedthegit>/radiosonde/libraries/SX1278FSK/ .
```
Redémarrer Arduino IDE
## Ajout carte esp32
Allez dans Outils-> type de cartes -> gestionnaire de cartes
puis dans la case taper esp32 et Installer.
## Dernière parties
Dans Outils -> Esp32 arduino: ->
Séléctionner "TTGO LoRa32-OLED v1"
Puis il vous faut ouvrir le fichier
RadioSonde_FSK.ino
Compiler et Téléverser le dans votre TTGO
puis il faut téléverser maintenant les DATA!
Dans Outils
cliquer sur ESP32 Sketch Data Upload
voila le TTGO est prêt!
Pour les futur mise à jour,
j'ai prévue une mise à jour directe via OTA depuis le TTGO donc
s'il est connecté à internet depuis votre Smartphone ou votre Box.
Cela se fera depuis le menu de la page Web.
73
Xavier

BIN
ant_cju.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

89
libraries/SD/.travis.yml Executable file
View File

@ -0,0 +1,89 @@
# default language
language: generic
env:
global:
- CLI_VERSION=latest
matrix:
include:
# compile example sketches for the following boards
- env:
- BOARD='arduino:avr:mega:cpu=atmega2560'
install:
- arduino-cli core install arduino:avr
- env:
- BOARD='arduino:samd:mkrzero'
install:
- arduino-cli core install arduino:samd
- env:
- BOARD='arduino:megaavr:uno2018:mode=on'
install:
- arduino-cli core install arduino:megaavr
- env:
- BOARD='arduino:sam:arduino_due_x'
install:
- arduino-cli core install arduino:sam
# check all code files for compliance with the Arduino code formatting style
- env:
- NAME='Code Formatting Check'
language: minimal
before_install:
# install Artistic Style code formatter tool: http://astyle.sourceforge.net
- |
mkdir "${HOME}/astyle";
wget --no-verbose --output-document="${HOME}/astyle/astyle.tar.gz" "https://iweb.dl.sourceforge.net/project/astyle/astyle/astyle%203.1/astyle_3.1_linux.tar.gz";
tar --extract --file="${HOME}/astyle/astyle.tar.gz" --directory="${HOME}/astyle";
cd "${HOME}/astyle/astyle/build/gcc";
make;
export PATH="$PWD/bin:$PATH";
cd "$TRAVIS_BUILD_DIR"
# download Arduino's Artistic Style configuration file
- wget --directory-prefix="${HOME}/astyle" https://raw.githubusercontent.com/arduino/Arduino/master/build/shared/examples_formatter.conf
script:
# check code formatting
- find . -regextype posix-extended -path './.git' -prune -or \( -iregex '.*\.((ino)|(h)|(hpp)|(hh)|(hxx)|(h\+\+)|(cpp)|(cc)|(cxx)|(c\+\+)|(cp)|(c)|(ipp)|(ii)|(ixx)|(inl)|(tpp)|(txx)|(tpl))$' -and -type f \) -print0 | xargs -0 -L1 bash -c 'if ! diff "$0" <(astyle --options=${HOME}/astyle/examples_formatter.conf --dry-run < "$0"); then echo "Non-compliant code formatting in $0"; false; fi'
# check all files for commonly misspelled words
- env:
- NAME='Spell Check'
language: python
python: 3.6
before_install:
# https://github.com/codespell-project/codespell
- pip install codespell
script:
# codespell will ignore any words in extras/codespell-ignore-words-list.txt, which may be used to fix false positives
- codespell --skip="${TRAVIS_BUILD_DIR}/.git" --ignore-words="${TRAVIS_BUILD_DIR}/extras/codespell-ignore-words-list.txt" "${TRAVIS_BUILD_DIR}"
# default phases shared by the compilation tests
before_install:
- wget http://downloads.arduino.cc/arduino-cli/arduino-cli-$CLI_VERSION-linux64.tar.bz2
- tar xf arduino-cli-$CLI_VERSION-linux64.tar.bz2
- mkdir -p "$HOME/bin"
- mv arduino-cli "$HOME/bin"
- export PATH="$PATH:$HOME/bin"
- arduino-cli core update-index
- buildExampleSketch() { arduino-cli compile --verbose --warnings all --fqbn $BOARD "$PWD/examples/$1"; }
- mkdir -p "$HOME/Arduino/libraries"
- ln -s "$PWD" "$HOME/Arduino/libraries/."
script:
- buildExampleSketch CardInfo
- buildExampleSketch Datalogger
- buildExampleSketch DumpFile
- buildExampleSketch Files
- buildExampleSketch listfiles
- buildExampleSketch ReadWrite
notifications:
webhooks:
# use TravisBuddy to comment on any pull request that results in a failed CI build
urls:
- https://www.travisbuddy.com/
on_success: never
on_failure: always

26
libraries/SD/README.adoc Executable file
View File

@ -0,0 +1,26 @@
= SD Library for Arduino =
image:https://travis-ci.org/arduino-libraries/SD.svg?branch=master[Build Status, link=https://travis-ci.org/arduino-libraries/SD]
The SD library allows for reading from and writing to SD cards.
For more information about this library please visit us at
http://www.arduino.cc/en/Reference/SD
== License ==
Copyright (C) 2009 by William Greiman
Copyright (c) 2010 SparkFun Electronics
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.

View File

@ -0,0 +1,116 @@
/*
SD card test
This example shows how use the utility libraries on which the'
SD library is based in order to get info about your SD card.
Very useful for testing a card when you're not sure whether its working or not.
The circuit:
SD card attached to SPI bus as follows:
** MOSI - pin 11 on Arduino Uno/Duemilanove/Diecimila
** MISO - pin 12 on Arduino Uno/Duemilanove/Diecimila
** CLK - pin 13 on Arduino Uno/Duemilanove/Diecimila
** CS - depends on your SD card shield or module.
Pin 4 used here for consistency with other Arduino examples
created 28 Mar 2011
by Limor Fried
modified 9 Apr 2012
by Tom Igoe
*/
// include the SD library:
#include <SPI.h>
#include <SD.h>
// set up variables using the SD utility library functions:
Sd2Card card;
SdVolume volume;
SdFile root;
// change this to match your SD shield or module;
// Arduino Ethernet shield: pin 4
// Adafruit SD shields and modules: pin 10
// Sparkfun SD shield: pin 8
// MKRZero SD: SDCARD_SS_PIN
const int chipSelect = 4;
void setup() {
// Open serial communications and wait for port to open:
Serial.begin(9600);
while (!Serial) {
; // wait for serial port to connect. Needed for native USB port only
}
Serial.print("\nInitializing SD card...");
// we'll use the initialization code from the utility libraries
// since we're just testing if the card is working!
if (!card.init(SPI_HALF_SPEED, chipSelect)) {
Serial.println("initialization failed. Things to check:");
Serial.println("* is a card inserted?");
Serial.println("* is your wiring correct?");
Serial.println("* did you change the chipSelect pin to match your shield or module?");
while (1);
} else {
Serial.println("Wiring is correct and a card is present.");
}
// print the type of card
Serial.println();
Serial.print("Card type: ");
switch (card.type()) {
case SD_CARD_TYPE_SD1:
Serial.println("SD1");
break;
case SD_CARD_TYPE_SD2:
Serial.println("SD2");
break;
case SD_CARD_TYPE_SDHC:
Serial.println("SDHC");
break;
default:
Serial.println("Unknown");
}
// Now we will try to open the 'volume'/'partition' - it should be FAT16 or FAT32
if (!volume.init(card)) {
Serial.println("Could not find FAT16/FAT32 partition.\nMake sure you've formatted the card");
while (1);
}
Serial.print("Clusters: ");
Serial.println(volume.clusterCount());
Serial.print("Blocks x Cluster: ");
Serial.println(volume.blocksPerCluster());
Serial.print("Total Blocks: ");
Serial.println(volume.blocksPerCluster() * volume.clusterCount());
Serial.println();
// print the type and size of the first FAT-type volume
uint32_t volumesize;
Serial.print("Volume type is: FAT");
Serial.println(volume.fatType(), DEC);
volumesize = volume.blocksPerCluster(); // clusters are collections of blocks
volumesize *= volume.clusterCount(); // we'll have a lot of clusters
volumesize /= 2; // SD card blocks are always 512 bytes (2 blocks are 1KB)
Serial.print("Volume size (Kb): ");
Serial.println(volumesize);
Serial.print("Volume size (Mb): ");
volumesize /= 1024;
Serial.println(volumesize);
Serial.print("Volume size (Gb): ");
Serial.println((float)volumesize / 1024.0);
Serial.println("\nFiles found on the card (name, date and size in bytes): ");
root.openRoot(volume);
// list all files in the card with date and size
root.ls(LS_R | LS_DATE | LS_SIZE);
}
void loop(void) {
}

View File

@ -0,0 +1,84 @@
/*
SD card datalogger
This example shows how to log data from three analog sensors
to an SD card using the SD library.
The circuit:
analog sensors on analog ins 0, 1, and 2
SD card attached to SPI bus as follows:
** MOSI - pin 11
** MISO - pin 12
** CLK - pin 13
** CS - pin 4 (for MKRZero SD: SDCARD_SS_PIN)
created 24 Nov 2010
modified 9 Apr 2012
by Tom Igoe
This example code is in the public domain.
*/
#include <SPI.h>
#include <SD.h>
const int chipSelect = 4;
void setup() {
// Open serial communications and wait for port to open:
Serial.begin(9600);
while (!Serial) {
; // wait for serial port to connect. Needed for native USB port only
}
Serial.print("Initializing SD card...");
// see if the card is present and can be initialized:
if (!SD.begin(chipSelect)) {
Serial.println("Card failed, or not present");
// don't do anything more:
while (1);
}
Serial.println("card initialized.");
}
void loop() {
// make a string for assembling the data to log:
String dataString = "";
// read three sensors and append to the string:
for (int analogPin = 0; analogPin < 3; analogPin++) {
int sensor = analogRead(analogPin);
dataString += String(sensor);
if (analogPin < 2) {
dataString += ",";
}
}
// open the file. note that only one file can be open at a time,
// so you have to close this one before opening another.
File dataFile = SD.open("datalog.txt", FILE_WRITE);
// if the file is available, write to it:
if (dataFile) {
dataFile.println(dataString);
dataFile.close();
// print to the serial port too:
Serial.println(dataString);
}
// if the file isn't open, pop up an error:
else {
Serial.println("error opening datalog.txt");
}
}

View File

@ -0,0 +1,65 @@
/*
SD card file dump
This example shows how to read a file from the SD card using the
SD library and send it over the serial port.
The circuit:
SD card attached to SPI bus as follows:
** MOSI - pin 11
** MISO - pin 12
** CLK - pin 13
** CS - pin 4 (for MKRZero SD: SDCARD_SS_PIN)
created 22 December 2010
by Limor Fried
modified 9 Apr 2012
by Tom Igoe
This example code is in the public domain.
*/
#include <SPI.h>
#include <SD.h>
const int chipSelect = 4;
void setup() {
// Open serial communications and wait for port to open:
Serial.begin(9600);
while (!Serial) {
; // wait for serial port to connect. Needed for native USB port only
}
Serial.print("Initializing SD card...");
// see if the card is present and can be initialized:
if (!SD.begin(chipSelect)) {
Serial.println("Card failed, or not present");
// don't do anything more:
while (1);
}
Serial.println("card initialized.");
// open the file. note that only one file can be open at a time,
// so you have to close this one before opening another.
File dataFile = SD.open("datalog.txt");
// if the file is available, write to it:
if (dataFile) {
while (dataFile.available()) {
Serial.write(dataFile.read());
}
dataFile.close();
}
// if the file isn't open, pop up an error:
else {
Serial.println("error opening datalog.txt");
}
}
void loop() {
}

View File

@ -0,0 +1,75 @@
/*
SD card basic file example
This example shows how to create and destroy an SD card file
The circuit:
SD card attached to SPI bus as follows:
** MOSI - pin 11
** MISO - pin 12
** CLK - pin 13
** CS - pin 4 (for MKRZero SD: SDCARD_SS_PIN)
created Nov 2010
by David A. Mellis
modified 9 Apr 2012
by Tom Igoe
This example code is in the public domain.
*/
#include <SPI.h>
#include <SD.h>
File myFile;
void setup() {
// Open serial communications and wait for port to open:
Serial.begin(9600);
while (!Serial) {
; // wait for serial port to connect. Needed for native USB port only
}
Serial.print("Initializing SD card...");
if (!SD.begin(4)) {
Serial.println("initialization failed!");
while (1);
}
Serial.println("initialization done.");
if (SD.exists("example.txt")) {
Serial.println("example.txt exists.");
} else {
Serial.println("example.txt doesn't exist.");
}
// open a new file and immediately close it:
Serial.println("Creating example.txt...");
myFile = SD.open("example.txt", FILE_WRITE);
myFile.close();
// Check to see if the file exists:
if (SD.exists("example.txt")) {
Serial.println("example.txt exists.");
} else {
Serial.println("example.txt doesn't exist.");
}
// delete the file:
Serial.println("Removing example.txt...");
SD.remove("example.txt");
if (SD.exists("example.txt")) {
Serial.println("example.txt exists.");
} else {
Serial.println("example.txt doesn't exist.");
}
}
void loop() {
// nothing happens after setup finishes.
}

View File

@ -0,0 +1,91 @@
/*
Non-blocking Write
This example demonstrates how to perform non-blocking writes
to a file on a SD card. The file will contain the current millis()
value every 10ms. If the SD card is busy, the data will be buffered
in order to not block the sketch.
NOTE: myFile.availableForWrite() will automatically sync the
file contents as needed. You may lose some unsynced data
still if myFile.sync() or myFile.close() is not called.
The circuit:
- Arduino MKR Zero board
- micro SD card attached
This example code is in the public domain.
*/
#include <SD.h>
// file name to use for writing
const char filename[] = "demo.txt";
// File object to represent file
File txtFile;
// string to buffer output
String buffer;
unsigned long lastMillis = 0;
void setup() {
Serial.begin(9600);
while (!Serial);
// reserve 1kB for String used as a buffer
buffer.reserve(1024);
// set LED pin to output, used to blink when writing
pinMode(LED_BUILTIN, OUTPUT);
// init the SD card
if (!SD.begin()) {
Serial.println("Card failed, or not present");
// don't do anything more:
while (1);
}
// If you want to start from an empty file,
// uncomment the next line:
// SD.remove(filename);
// try to open the file for writing
txtFile = SD.open(filename, FILE_WRITE);
if (!txtFile) {
Serial.print("error opening ");
Serial.println(filename);
while (1);
}
// add some new lines to start
txtFile.println();
txtFile.println("Hello World!");
}
void loop() {
// check if it's been over 10 ms since the last line added
unsigned long now = millis();
if ((now - lastMillis) >= 10) {
// add a new line to the buffer
buffer += "Hello ";
buffer += now;
buffer += "\r\n";
lastMillis = now;
}
// check if the SD card is available to write data without blocking
// and if the buffered data is enough for the full chunk size
unsigned int chunkSize = txtFile.availableForWrite();
if (chunkSize && buffer.length() >= chunkSize) {
// write to file and blink LED
digitalWrite(LED_BUILTIN, HIGH);
txtFile.write(buffer.c_str(), chunkSize);
digitalWrite(LED_BUILTIN, LOW);
// remove written data from buffer
buffer.remove(0, chunkSize);
}
}

View File

@ -0,0 +1,79 @@
/*
SD card read/write
This example shows how to read and write data to and from an SD card file
The circuit:
SD card attached to SPI bus as follows:
** MOSI - pin 11
** MISO - pin 12
** CLK - pin 13
** CS - pin 4 (for MKRZero SD: SDCARD_SS_PIN)
created Nov 2010
by David A. Mellis
modified 9 Apr 2012
by Tom Igoe
This example code is in the public domain.
*/
#include <SPI.h>
#include <SD.h>
File myFile;
void setup() {
// Open serial communications and wait for port to open:
Serial.begin(9600);
while (!Serial) {
; // wait for serial port to connect. Needed for native USB port only
}
Serial.print("Initializing SD card...");
if (!SD.begin(4)) {
Serial.println("initialization failed!");
while (1);
}
Serial.println("initialization done.");
// open the file. note that only one file can be open at a time,
// so you have to close this one before opening another.
myFile = SD.open("test.txt", FILE_WRITE);
// if the file opened okay, write to it:
if (myFile) {
Serial.print("Writing to test.txt...");
myFile.println("testing 1, 2, 3.");
// close the file:
myFile.close();
Serial.println("done.");
} else {
// if the file didn't open, print an error:
Serial.println("error opening test.txt");
}
// re-open the file for reading:
myFile = SD.open("test.txt");
if (myFile) {
Serial.println("test.txt:");
// read from the file until there's nothing else in it:
while (myFile.available()) {
Serial.write(myFile.read());
}
// close the file:
myFile.close();
} else {
// if the file didn't open, print an error:
Serial.println("error opening test.txt");
}
}
void loop() {
// nothing happens after setup
}

View File

@ -0,0 +1,80 @@
/*
Listfiles
This example shows how print out the files in a
directory on a SD card
The circuit:
SD card attached to SPI bus as follows:
** MOSI - pin 11
** MISO - pin 12
** CLK - pin 13
** CS - pin 4 (for MKRZero SD: SDCARD_SS_PIN)
created Nov 2010
by David A. Mellis
modified 9 Apr 2012
by Tom Igoe
modified 2 Feb 2014
by Scott Fitzgerald
This example code is in the public domain.
*/
#include <SPI.h>
#include <SD.h>
File root;
void setup() {
// Open serial communications and wait for port to open:
Serial.begin(9600);
while (!Serial) {
; // wait for serial port to connect. Needed for native USB port only
}
Serial.print("Initializing SD card...");
if (!SD.begin(4)) {
Serial.println("initialization failed!");
while (1);
}
Serial.println("initialization done.");
root = SD.open("/");
printDirectory(root, 0);
Serial.println("done!");
}
void loop() {
// nothing happens after setup finishes.
}
void printDirectory(File dir, int numTabs) {
while (true) {
File entry = dir.openNextFile();
if (! entry) {
// no more files
break;
}
for (uint8_t i = 0; i < numTabs; i++) {
Serial.print('\t');
}
Serial.print(entry.name());
if (entry.isDirectory()) {
Serial.println("/");
printDirectory(entry, numTabs + 1);
} else {
// files have sizes, directories do not
Serial.print("\t\t");
Serial.println(entry.size(), DEC);
}
entry.close();
}
}

31
libraries/SD/keywords.txt Executable file
View File

@ -0,0 +1,31 @@
#######################################
# Syntax Coloring Map SD
#######################################
#######################################
# Datatypes (KEYWORD1)
#######################################
SD KEYWORD1 SD
File KEYWORD1 SD
SDFile KEYWORD1 SD
#######################################
# Methods and Functions (KEYWORD2)
#######################################
begin KEYWORD2
exists KEYWORD2
mkdir KEYWORD2
remove KEYWORD2
rmdir KEYWORD2
open KEYWORD2
close KEYWORD2
seek KEYWORD2
position KEYWORD2
size KEYWORD2
#######################################
# Constants (LITERAL1)
#######################################
FILE_READ LITERAL1
FILE_WRITE LITERAL1

View File

@ -0,0 +1,9 @@
name=SD
version=1.2.4
author=Arduino, SparkFun
maintainer=Arduino <info@arduino.cc>
sentence=Enables reading and writing on SD cards.
paragraph=Once an SD memory card is connected to the SPI interface of the Arduino or Genuino board you can create files and read/write on them. You can also move through directories on the SD card.
category=Data Storage
url=http://www.arduino.cc/en/Reference/SD
architectures=*

168
libraries/SD/src/File.cpp Executable file
View File

@ -0,0 +1,168 @@
/*
SD - a slightly more friendly wrapper for sdfatlib
This library aims to expose a subset of SD card functionality
in the form of a higher level "wrapper" object.
License: GNU General Public License V3
(Because sdfatlib is licensed with this.)
(C) Copyright 2010 SparkFun Electronics
*/
#include <SD.h>
/* for debugging file open/close leaks
uint8_t nfilecount=0;
*/
File::File(SdFile f, const char *n) {
// oh man you are kidding me, new() doesn't exist? Ok we do it by hand!
_file = (SdFile *)malloc(sizeof(SdFile));
if (_file) {
memcpy(_file, &f, sizeof(SdFile));
strncpy(_name, n, 12);
_name[12] = 0;
/* for debugging file open/close leaks
nfilecount++;
Serial.print("Created \"");
Serial.print(n);
Serial.print("\": ");
Serial.println(nfilecount, DEC);
*/
}
}
File::File(void) {
_file = 0;
_name[0] = 0;
//Serial.print("Created empty file object");
}
// returns a pointer to the file name
char *File::name(void) {
return _name;
}
// a directory is a special type of file
boolean File::isDirectory(void) {
return (_file && _file->isDir());
}
size_t File::write(uint8_t val) {
return write(&val, 1);
}
size_t File::write(const uint8_t *buf, size_t size) {
size_t t;
if (!_file) {
setWriteError();
return 0;
}
_file->clearWriteError();
t = _file->write(buf, size);
if (_file->getWriteError()) {
setWriteError();
return 0;
}
return t;
}
int File::availableForWrite() {
if (_file) {
return _file->availableForWrite();
}
return 0;
}
int File::peek() {
if (! _file) {
return 0;
}
int c = _file->read();
if (c != -1) {
_file->seekCur(-1);
}
return c;
}
int File::read() {
if (_file) {
return _file->read();
}
return -1;
}
// buffered read for more efficient, high speed reading
int File::read(void *buf, uint16_t nbyte) {
if (_file) {
return _file->read(buf, nbyte);
}
return 0;
}
int File::available() {
if (! _file) {
return 0;
}
uint32_t n = size() - position();
return n > 0X7FFF ? 0X7FFF : n;
}
void File::flush() {
if (_file) {
_file->sync();
}
}
boolean File::seek(uint32_t pos) {
if (! _file) {
return false;
}
return _file->seekSet(pos);
}
uint32_t File::position() {
if (! _file) {
return -1;
}
return _file->curPosition();
}
uint32_t File::size() {
if (! _file) {
return 0;
}
return _file->fileSize();
}
void File::close() {
if (_file) {
_file->close();
free(_file);
_file = 0;
/* for debugging file open/close leaks
nfilecount--;
Serial.print("Deleted ");
Serial.println(nfilecount, DEC);
*/
}
}
File::operator bool() {
if (_file) {
return _file->isOpen();
}
return false;
}

13
libraries/SD/src/README.txt Executable file
View File

@ -0,0 +1,13 @@
** SD - a slightly more friendly wrapper for sdfatlib **
This library aims to expose a subset of SD card functionality in the
form of a higher level "wrapper" object.
License: GNU General Public License V3
(Because sdfatlib is licensed with this.)
(C) Copyright 2010 SparkFun Electronics
Now better than ever with optimization, multiple file support, directory handling, etc - ladyada!

637
libraries/SD/src/SD.cpp Executable file
View File

@ -0,0 +1,637 @@
/*
SD - a slightly more friendly wrapper for sdfatlib
This library aims to expose a subset of SD card functionality
in the form of a higher level "wrapper" object.
License: GNU General Public License V3
(Because sdfatlib is licensed with this.)
(C) Copyright 2010 SparkFun Electronics
This library provides four key benefits:
Including `SD.h` automatically creates a global
`SD` object which can be interacted with in a similar
manner to other standard global objects like `Serial` and `Ethernet`.
Boilerplate initialisation code is contained in one method named
`begin` and no further objects need to be created in order to access
the SD card.
Calls to `open` can supply a full path name including parent
directories which simplifies interacting with files in subdirectories.
Utility methods are provided to determine whether a file exists
and to create a directory hierarchy.
Note however that not all functionality provided by the underlying
sdfatlib library is exposed.
*/
/*
Implementation Notes
In order to handle multi-directory path traversal, functionality that
requires this ability is implemented as callback functions.
Individual methods call the `walkPath` function which performs the actual
directory traversal (swapping between two different directory/file handles
along the way) and at each level calls the supplied callback function.
Some types of functionality will take an action at each level (e.g. exists
or make directory) which others will only take an action at the bottom
level (e.g. open).
*/
#include "SD.h"
namespace SDLib {
// Used by `getNextPathComponent`
#define MAX_COMPONENT_LEN 12 // What is max length?
#define PATH_COMPONENT_BUFFER_LEN MAX_COMPONENT_LEN+1
bool getNextPathComponent(const char *path, unsigned int *p_offset,
char *buffer) {
/*
Parse individual path components from a path.
e.g. after repeated calls '/foo/bar/baz' will be split
into 'foo', 'bar', 'baz'.
This is similar to `strtok()` but copies the component into the
supplied buffer rather than modifying the original string.
`buffer` needs to be PATH_COMPONENT_BUFFER_LEN in size.
`p_offset` needs to point to an integer of the offset at
which the previous path component finished.
Returns `true` if more components remain.
Returns `false` if this is the last component.
(This means path ended with 'foo' or 'foo/'.)
*/
// TODO: Have buffer local to this function, so we know it's the
// correct length?
int bufferOffset = 0;
int offset = *p_offset;
// Skip root or other separator
if (path[offset] == '/') {
offset++;
}
// Copy the next next path segment
while (bufferOffset < MAX_COMPONENT_LEN
&& (path[offset] != '/')
&& (path[offset] != '\0')) {
buffer[bufferOffset++] = path[offset++];
}
buffer[bufferOffset] = '\0';
// Skip trailing separator so we can determine if this
// is the last component in the path or not.
if (path[offset] == '/') {
offset++;
}
*p_offset = offset;
return (path[offset] != '\0');
}
boolean walkPath(const char *filepath, SdFile& parentDir,
boolean(*callback)(SdFile& parentDir,
const char *filePathComponent,
boolean isLastComponent,
void *object),
void *object = NULL) {
/*
When given a file path (and parent directory--normally root),
this function traverses the directories in the path and at each
level calls the supplied callback function while also providing
the supplied object for context if required.
e.g. given the path '/foo/bar/baz'
the callback would be called at the equivalent of
'/foo', '/foo/bar' and '/foo/bar/baz'.
The implementation swaps between two different directory/file
handles as it traverses the directories and does not use recursion
in an attempt to use memory efficiently.
If a callback wishes to stop the directory traversal it should
return false--in this case the function will stop the traversal,
tidy up and return false.
If a directory path doesn't exist at some point this function will
also return false and not subsequently call the callback.
If a directory path specified is complete, valid and the callback
did not indicate the traversal should be interrupted then this
function will return true.
*/
SdFile subfile1;
SdFile subfile2;
char buffer[PATH_COMPONENT_BUFFER_LEN];
unsigned int offset = 0;
SdFile *p_parent;
SdFile *p_child;
SdFile *p_tmp_sdfile;
p_child = &subfile1;
p_parent = &parentDir;
while (true) {
boolean moreComponents = getNextPathComponent(filepath, &offset, buffer);
boolean shouldContinue = callback((*p_parent), buffer, !moreComponents, object);
if (!shouldContinue) {
// TODO: Don't repeat this code?
// If it's one we've created then we
// don't need the parent handle anymore.
if (p_parent != &parentDir) {
(*p_parent).close();
}
return false;
}
if (!moreComponents) {
break;
}
boolean exists = (*p_child).open(*p_parent, buffer, O_RDONLY);
// If it's one we've created then we
// don't need the parent handle anymore.
if (p_parent != &parentDir) {
(*p_parent).close();
}
// Handle case when it doesn't exist and we can't continue...
if (exists) {
// We alternate between two file handles as we go down
// the path.
if (p_parent == &parentDir) {
p_parent = &subfile2;
}
p_tmp_sdfile = p_parent;
p_parent = p_child;
p_child = p_tmp_sdfile;
} else {
return false;
}
}
if (p_parent != &parentDir) {
(*p_parent).close(); // TODO: Return/ handle different?
}
return true;
}
/*
The callbacks used to implement various functionality follow.
Each callback is supplied with a parent directory handle,
character string with the name of the current file path component,
a flag indicating if this component is the last in the path and
a pointer to an arbitrary object used for context.
*/
boolean callback_pathExists(SdFile& parentDir, const char *filePathComponent,
boolean /* isLastComponent */, void * /* object */) {
/*
Callback used to determine if a file/directory exists in parent
directory.
Returns true if file path exists.
*/
SdFile child;
boolean exists = child.open(parentDir, filePathComponent, O_RDONLY);
if (exists) {
child.close();
}
return exists;
}
boolean callback_makeDirPath(SdFile& parentDir, const char *filePathComponent,
boolean isLastComponent, void *object) {
/*
Callback used to create a directory in the parent directory if
it does not already exist.
Returns true if a directory was created or it already existed.
*/
boolean result = false;
SdFile child;
result = callback_pathExists(parentDir, filePathComponent, isLastComponent, object);
if (!result) {
result = child.makeDir(parentDir, filePathComponent);
}
return result;
}
/*
boolean callback_openPath(SdFile& parentDir, char *filePathComponent,
boolean isLastComponent, void *object) {
Callback used to open a file specified by a filepath that may
specify one or more directories above it.
Expects the context object to be an instance of `SDClass` and
will use the `file` property of the instance to open the requested
file/directory with the associated file open mode property.
Always returns true if the directory traversal hasn't reached the
bottom of the directory hierarchy.
Returns false once the file has been opened--to prevent the traversal
from descending further. (This may be unnecessary.)
if (isLastComponent) {
SDClass *p_SD = static_cast<SDClass*>(object);
p_SD->file.open(parentDir, filePathComponent, p_SD->fileOpenMode);
if (p_SD->fileOpenMode == FILE_WRITE) {
p_SD->file.seekSet(p_SD->file.fileSize());
}
// TODO: Return file open result?
return false;
}
return true;
}
*/
boolean callback_remove(SdFile& parentDir, const char *filePathComponent,
boolean isLastComponent, void * /* object */) {
if (isLastComponent) {
return SdFile::remove(parentDir, filePathComponent);
}
return true;
}
boolean callback_rmdir(SdFile& parentDir, const char *filePathComponent,
boolean isLastComponent, void * /* object */) {
if (isLastComponent) {
SdFile f;
if (!f.open(parentDir, filePathComponent, O_READ)) {
return false;
}
return f.rmDir();
}
return true;
}
/* Implementation of class used to create `SDCard` object. */
boolean SDClass::begin(uint8_t csPin) {
if (root.isOpen()) {
root.close();
}
/*
Performs the initialisation required by the sdfatlib library.
Return true if initialization succeeds, false otherwise.
*/
return card.init(SPI_HALF_SPEED, csPin) &&
volume.init(card) &&
root.openRoot(volume);
}
boolean SDClass::begin(uint32_t clock, uint8_t csPin) {
if (root.isOpen()) {
root.close();
}
return card.init(SPI_HALF_SPEED, csPin) &&
card.setSpiClock(clock) &&
volume.init(card) &&
root.openRoot(volume);
}
//call this when a card is removed. It will allow you to insert and initialise a new card.
void SDClass::end() {
root.close();
}
// this little helper is used to traverse paths
SdFile SDClass::getParentDir(const char *filepath, int *index) {
// get parent directory
SdFile d1;
SdFile d2;
d1.openRoot(volume); // start with the mostparent, root!
// we'll use the pointers to swap between the two objects
SdFile *parent = &d1;
SdFile *subdir = &d2;
const char *origpath = filepath;
while (strchr(filepath, '/')) {
// get rid of leading /'s
if (filepath[0] == '/') {
filepath++;
continue;
}
if (! strchr(filepath, '/')) {
// it was in the root directory, so leave now
break;
}
// extract just the name of the next subdirectory
uint8_t idx = strchr(filepath, '/') - filepath;
if (idx > 12) {
idx = 12; // don't let them specify long names
}
char subdirname[13];
strncpy(subdirname, filepath, idx);
subdirname[idx] = 0;
// close the subdir (we reuse them) if open
subdir->close();
if (! subdir->open(parent, subdirname, O_READ)) {
// failed to open one of the subdirectories
return SdFile();
}
// move forward to the next subdirectory
filepath += idx;
// we reuse the objects, close it.
parent->close();
// swap the pointers
SdFile *t = parent;
parent = subdir;
subdir = t;
}
*index = (int)(filepath - origpath);
// parent is now the parent directory of the file!
return *parent;
}
File SDClass::open(const char *filepath, uint8_t mode) {
/*
Open the supplied file path for reading or writing.
The file content can be accessed via the `file` property of
the `SDClass` object--this property is currently
a standard `SdFile` object from `sdfatlib`.
Defaults to read only.
If `write` is true, default action (when `append` is true) is to
append data to the end of the file.
If `append` is false then the file will be truncated first.
If the file does not exist and it is opened for writing the file
will be created.
An attempt to open a file for reading that does not exist is an
error.
*/
int pathidx;
// do the interactive search
SdFile parentdir = getParentDir(filepath, &pathidx);
// no more subdirs!
filepath += pathidx;
if (! filepath[0]) {
// it was the directory itself!
return File(parentdir, "/");
}
// Open the file itself
SdFile file;
// failed to open a subdir!
if (!parentdir.isOpen()) {
return File();
}
if (! file.open(parentdir, filepath, mode)) {
return File();
}
// close the parent
parentdir.close();
if ((mode & (O_APPEND | O_WRITE)) == (O_APPEND | O_WRITE)) {
file.seekSet(file.fileSize());
}
return File(file, filepath);
}
/*
File SDClass::open(char *filepath, uint8_t mode) {
//
Open the supplied file path for reading or writing.
The file content can be accessed via the `file` property of
the `SDClass` object--this property is currently
a standard `SdFile` object from `sdfatlib`.
Defaults to read only.
If `write` is true, default action (when `append` is true) is to
append data to the end of the file.
If `append` is false then the file will be truncated first.
If the file does not exist and it is opened for writing the file
will be created.
An attempt to open a file for reading that does not exist is an
error.
//
// TODO: Allow for read&write? (Possibly not, as it requires seek.)
fileOpenMode = mode;
walkPath(filepath, root, callback_openPath, this);
return File();
}
*/
//boolean SDClass::close() {
// /*
//
// Closes the file opened by the `open` method.
//
// */
// file.close();
//}
boolean SDClass::exists(const char *filepath) {
/*
Returns true if the supplied file path exists.
*/
return walkPath(filepath, root, callback_pathExists);
}
//boolean SDClass::exists(char *filepath, SdFile& parentDir) {
// /*
//
// Returns true if the supplied file path rooted at `parentDir`
// exists.
//
// */
// return walkPath(filepath, parentDir, callback_pathExists);
//}
boolean SDClass::mkdir(const char *filepath) {
/*
Makes a single directory or a hierarchy of directories.
A rough equivalent to `mkdir -p`.
*/
return walkPath(filepath, root, callback_makeDirPath);
}
boolean SDClass::rmdir(const char *filepath) {
/*
Remove a single directory or a hierarchy of directories.
A rough equivalent to `rm -rf`.
*/
return walkPath(filepath, root, callback_rmdir);
}
boolean SDClass::remove(const char *filepath) {
return walkPath(filepath, root, callback_remove);
}
// allows you to recurse into a directory
File File::openNextFile(uint8_t mode) {
dir_t p;
//Serial.print("\t\treading dir...");
while (_file->readDir(&p) > 0) {
// done if past last used entry
if (p.name[0] == DIR_NAME_FREE) {
//Serial.println("end");
return File();
}
// skip deleted entry and entries for . and ..
if (p.name[0] == DIR_NAME_DELETED || p.name[0] == '.') {
//Serial.println("dots");
continue;
}
// only list subdirectories and files
if (!DIR_IS_FILE_OR_SUBDIR(&p)) {
//Serial.println("notafile");
continue;
}
// print file name with possible blank fill
SdFile f;
char name[13];
_file->dirName(p, name);
//Serial.print("try to open file ");
//Serial.println(name);
if (f.open(_file, name, mode)) {
//Serial.println("OK!");
return File(f, name);
} else {
//Serial.println("ugh");
return File();
}
}
//Serial.println("nothing");
return File();
}
void File::rewindDirectory(void) {
if (isDirectory()) {
_file->rewind();
}
}
SDClass SD;
};

138
libraries/SD/src/SD.h Executable file
View File

@ -0,0 +1,138 @@
/*
SD - a slightly more friendly wrapper for sdfatlib
This library aims to expose a subset of SD card functionality
in the form of a higher level "wrapper" object.
License: GNU General Public License V3
(Because sdfatlib is licensed with this.)
(C) Copyright 2010 SparkFun Electronics
*/
#ifndef __SD_H__
#define __SD_H__
#include <Arduino.h>
#include "utility/SdFat.h"
#include "utility/SdFatUtil.h"
#define FILE_READ O_READ
#define FILE_WRITE (O_READ | O_WRITE | O_CREAT | O_APPEND)
namespace SDLib {
class File : public Stream {
private:
char _name[13]; // our name
SdFile *_file; // underlying file pointer
public:
File(SdFile f, const char *name); // wraps an underlying SdFile
File(void); // 'empty' constructor
virtual size_t write(uint8_t);
virtual size_t write(const uint8_t *buf, size_t size);
virtual int availableForWrite();
virtual int read();
virtual int peek();
virtual int available();
virtual void flush();
int read(void *buf, uint16_t nbyte);
boolean seek(uint32_t pos);
uint32_t position();
uint32_t size();
void close();
operator bool();
char * name();
boolean isDirectory(void);
File openNextFile(uint8_t mode = O_RDONLY);
void rewindDirectory(void);
using Print::write;
};
class SDClass {
private:
// These are required for initialisation and use of sdfatlib
Sd2Card card;
SdVolume volume;
SdFile root;
// my quick&dirty iterator, should be replaced
SdFile getParentDir(const char *filepath, int *indx);
public:
// This needs to be called to set up the connection to the SD card
// before other methods are used.
boolean begin(uint8_t csPin = SD_CHIP_SELECT_PIN);
boolean begin(uint32_t clock, uint8_t csPin);
//call this when a card is removed. It will allow you to insert and initialise a new card.
void end();
// Open the specified file/directory with the supplied mode (e.g. read or
// write, etc). Returns a File object for interacting with the file.
// Note that currently only one file can be open at a time.
File open(const char *filename, uint8_t mode = FILE_READ);
File open(const String &filename, uint8_t mode = FILE_READ) {
return open(filename.c_str(), mode);
}
// Methods to determine if the requested file path exists.
boolean exists(const char *filepath);
boolean exists(const String &filepath) {
return exists(filepath.c_str());
}
// Create the requested directory heirarchy--if intermediate directories
// do not exist they will be created.
boolean mkdir(const char *filepath);
boolean mkdir(const String &filepath) {
return mkdir(filepath.c_str());
}
// Delete the file.
boolean remove(const char *filepath);
boolean remove(const String &filepath) {
return remove(filepath.c_str());
}
boolean rmdir(const char *filepath);
boolean rmdir(const String &filepath) {
return rmdir(filepath.c_str());
}
private:
// This is used to determine the mode used to open a file
// it's here because it's the easiest place to pass the
// information through the directory walking function. But
// it's probably not the best place for it.
// It shouldn't be set directly--it is set via the parameters to `open`.
int fileOpenMode;
friend class File;
friend boolean callback_openPath(SdFile&, const char *, boolean, void *);
};
extern SDClass SD;
};
// We enclose File and SD classes in namespace SDLib to avoid conflicts
// with others legacy libraries that redefines File class.
// This ensure compatibility with sketches that uses only SD library
using namespace SDLib;
// This allows sketches to use SDLib::File with other libraries (in the
// sketch you must use SDFile instead of File to disambiguate)
typedef SDLib::File SDFile;
typedef SDLib::SDClass SDFileSystemClass;
#define SDFileSystem SDLib::SD
#endif

View File

@ -0,0 +1,418 @@
/* Arduino SdFat Library
Copyright (C) 2009 by William Greiman
This file is part of the Arduino SdFat Library
This Library is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with the Arduino SdFat Library. If not, see
<http://www.gnu.org/licenses/>.
*/
#ifndef FatStructs_h
#define FatStructs_h
/**
\file
FAT file structures
*/
/*
mostly from Microsoft document fatgen103.doc
http://www.microsoft.com/whdc/system/platform/firmware/fatgen.mspx
*/
//------------------------------------------------------------------------------
/** Value for byte 510 of boot block or MBR */
uint8_t const BOOTSIG0 = 0X55;
/** Value for byte 511 of boot block or MBR */
uint8_t const BOOTSIG1 = 0XAA;
//------------------------------------------------------------------------------
/**
\struct partitionTable
\brief MBR partition table entry
A partition table entry for a MBR formatted storage device.
The MBR partition table has four entries.
*/
struct partitionTable {
/**
Boot Indicator . Indicates whether the volume is the active
partition. Legal values include: 0X00. Do not use for booting.
0X80 Active partition.
*/
uint8_t boot;
/**
Head part of Cylinder-head-sector address of the first block in
the partition. Legal values are 0-255. Only used in old PC BIOS.
*/
uint8_t beginHead;
/**
Sector part of Cylinder-head-sector address of the first block in
the partition. Legal values are 1-63. Only used in old PC BIOS.
*/
unsigned beginSector : 6;
/** High bits cylinder for first block in partition. */
unsigned beginCylinderHigh : 2;
/**
Combine beginCylinderLow with beginCylinderHigh. Legal values
are 0-1023. Only used in old PC BIOS.
*/
uint8_t beginCylinderLow;
/**
Partition type. See defines that begin with PART_TYPE_ for
some Microsoft partition types.
*/
uint8_t type;
/**
head part of cylinder-head-sector address of the last sector in the
partition. Legal values are 0-255. Only used in old PC BIOS.
*/
uint8_t endHead;
/**
Sector part of cylinder-head-sector address of the last sector in
the partition. Legal values are 1-63. Only used in old PC BIOS.
*/
unsigned endSector : 6;
/** High bits of end cylinder */
unsigned endCylinderHigh : 2;
/**
Combine endCylinderLow with endCylinderHigh. Legal values
are 0-1023. Only used in old PC BIOS.
*/
uint8_t endCylinderLow;
/** Logical block address of the first block in the partition. */
uint32_t firstSector;
/** Length of the partition, in blocks. */
uint32_t totalSectors;
} __attribute__((packed));
/** Type name for partitionTable */
typedef struct partitionTable part_t;
//------------------------------------------------------------------------------
/**
\struct masterBootRecord
\brief Master Boot Record
The first block of a storage device that is formatted with a MBR.
*/
struct masterBootRecord {
/** Code Area for master boot program. */
uint8_t codeArea[440];
/** Optional WindowsNT disk signature. May contain more boot code. */
uint32_t diskSignature;
/** Usually zero but may be more boot code. */
uint16_t usuallyZero;
/** Partition tables. */
part_t part[4];
/** First MBR signature byte. Must be 0X55 */
uint8_t mbrSig0;
/** Second MBR signature byte. Must be 0XAA */
uint8_t mbrSig1;
} __attribute__((packed));
/** Type name for masterBootRecord */
typedef struct masterBootRecord mbr_t;
//------------------------------------------------------------------------------
/**
\struct biosParmBlock
\brief BIOS parameter block
The BIOS parameter block describes the physical layout of a FAT volume.
*/
struct biosParmBlock {
/**
Count of bytes per sector. This value may take on only the
following values: 512, 1024, 2048 or 4096
*/
uint16_t bytesPerSector;
/**
Number of sectors per allocation unit. This value must be a
power of 2 that is greater than 0. The legal values are
1, 2, 4, 8, 16, 32, 64, and 128.
*/
uint8_t sectorsPerCluster;
/**
Number of sectors before the first FAT.
This value must not be zero.
*/
uint16_t reservedSectorCount;
/** The count of FAT data structures on the volume. This field should
always contain the value 2 for any FAT volume of any type.
*/
uint8_t fatCount;
/**
For FAT12 and FAT16 volumes, this field contains the count of
32-byte directory entries in the root directory. For FAT32 volumes,
this field must be set to 0. For FAT12 and FAT16 volumes, this
value should always specify a count that when multiplied by 32
results in a multiple of bytesPerSector. FAT16 volumes should
use the value 512.
*/
uint16_t rootDirEntryCount;
/**
This field is the old 16-bit total count of sectors on the volume.
This count includes the count of all sectors in all four regions
of the volume. This field can be 0; if it is 0, then totalSectors32
must be non-zero. For FAT32 volumes, this field must be 0. For
FAT12 and FAT16 volumes, this field contains the sector count, and
totalSectors32 is 0 if the total sector count fits
(is less than 0x10000).
*/
uint16_t totalSectors16;
/**
This dates back to the old MS-DOS 1.x media determination and is
no longer usually used for anything. 0xF8 is the standard value
for fixed (non-removable) media. For removable media, 0xF0 is
frequently used. Legal values are 0xF0 or 0xF8-0xFF.
*/
uint8_t mediaType;
/**
Count of sectors occupied by one FAT on FAT12/FAT16 volumes.
On FAT32 volumes this field must be 0, and sectorsPerFat32
contains the FAT size count.
*/
uint16_t sectorsPerFat16;
/** Sectors per track for interrupt 0x13. Not used otherwise. */
uint16_t sectorsPerTrtack;
/** Number of heads for interrupt 0x13. Not used otherwise. */
uint16_t headCount;
/**
Count of hidden sectors preceding the partition that contains this
FAT volume. This field is generally only relevant for media
visible on interrupt 0x13.
*/
uint32_t hidddenSectors;
/**
This field is the new 32-bit total count of sectors on the volume.
This count includes the count of all sectors in all four regions
of the volume. This field can be 0; if it is 0, then
totalSectors16 must be non-zero.
*/
uint32_t totalSectors32;
/**
Count of sectors occupied by one FAT on FAT32 volumes.
*/
uint32_t sectorsPerFat32;
/**
This field is only defined for FAT32 media and does not exist on
FAT12 and FAT16 media.
Bits 0-3 -- Zero-based number of active FAT.
Only valid if mirroring is disabled.
Bits 4-6 -- Reserved.
Bit 7 -- 0 means the FAT is mirrored at runtime into all FATs.
-- 1 means only one FAT is active; it is the one referenced in bits 0-3.
Bits 8-15 -- Reserved.
*/
uint16_t fat32Flags;
/**
FAT32 version. High byte is major revision number.
Low byte is minor revision number. Only 0.0 define.
*/
uint16_t fat32Version;
/**
Cluster number of the first cluster of the root directory for FAT32.
This usually 2 but not required to be 2.
*/
uint32_t fat32RootCluster;
/**
Sector number of FSINFO structure in the reserved area of the
FAT32 volume. Usually 1.
*/
uint16_t fat32FSInfo;
/**
If non-zero, indicates the sector number in the reserved area
of the volume of a copy of the boot record. Usually 6.
No value other than 6 is recommended.
*/
uint16_t fat32BackBootBlock;
/**
Reserved for future expansion. Code that formats FAT32 volumes
should always set all of the bytes of this field to 0.
*/
uint8_t fat32Reserved[12];
} __attribute__((packed));
/** Type name for biosParmBlock */
typedef struct biosParmBlock bpb_t;
//------------------------------------------------------------------------------
/**
\struct fat32BootSector
\brief Boot sector for a FAT16 or FAT32 volume.
*/
struct fat32BootSector {
/** X86 jmp to boot program */
uint8_t jmpToBootCode[3];
/** informational only - don't depend on it */
char oemName[8];
/** BIOS Parameter Block */
bpb_t bpb;
/** for int0x13 use value 0X80 for hard drive */
uint8_t driveNumber;
/** used by Windows NT - should be zero for FAT */
uint8_t reserved1;
/** 0X29 if next three fields are valid */
uint8_t bootSignature;
/** usually generated by combining date and time */
uint32_t volumeSerialNumber;
/** should match volume label in root dir */
char volumeLabel[11];
/** informational only - don't depend on it */
char fileSystemType[8];
/** X86 boot code */
uint8_t bootCode[420];
/** must be 0X55 */
uint8_t bootSectorSig0;
/** must be 0XAA */
uint8_t bootSectorSig1;
} __attribute__((packed));
//------------------------------------------------------------------------------
// End Of Chain values for FAT entries
/** FAT16 end of chain value used by Microsoft. */
uint16_t const FAT16EOC = 0XFFFF;
/** Minimum value for FAT16 EOC. Use to test for EOC. */
uint16_t const FAT16EOC_MIN = 0XFFF8;
/** FAT32 end of chain value used by Microsoft. */
uint32_t const FAT32EOC = 0X0FFFFFFF;
/** Minimum value for FAT32 EOC. Use to test for EOC. */
uint32_t const FAT32EOC_MIN = 0X0FFFFFF8;
/** Mask a for FAT32 entry. Entries are 28 bits. */
uint32_t const FAT32MASK = 0X0FFFFFFF;
/** Type name for fat32BootSector */
typedef struct fat32BootSector fbs_t;
//------------------------------------------------------------------------------
/**
\struct directoryEntry
\brief FAT short directory entry
Short means short 8.3 name, not the entry size.
Date Format. A FAT directory entry date stamp is a 16-bit field that is
basically a date relative to the MS-DOS epoch of 01/01/1980. Here is the
format (bit 0 is the LSB of the 16-bit word, bit 15 is the MSB of the
16-bit word):
Bits 9-15: Count of years from 1980, valid value range 0-127
inclusive (1980-2107).
Bits 5-8: Month of year, 1 = January, valid value range 1-12 inclusive.
Bits 0-4: Day of month, valid value range 1-31 inclusive.
Time Format. A FAT directory entry time stamp is a 16-bit field that has
a granularity of 2 seconds. Here is the format (bit 0 is the LSB of the
16-bit word, bit 15 is the MSB of the 16-bit word).
Bits 11-15: Hours, valid value range 0-23 inclusive.
Bits 5-10: Minutes, valid value range 0-59 inclusive.
Bits 0-4: 2-second count, valid value range 0-29 inclusive (0 - 58 seconds).
The valid time range is from Midnight 00:00:00 to 23:59:58.
*/
struct directoryEntry {
/**
Short 8.3 name.
The first eight bytes contain the file name with blank fill.
The last three bytes contain the file extension with blank fill.
*/
uint8_t name[11];
/** Entry attributes.
The upper two bits of the attribute byte are reserved and should
always be set to 0 when a file is created and never modified or
looked at after that. See defines that begin with DIR_ATT_.
*/
uint8_t attributes;
/**
Reserved for use by Windows NT. Set value to 0 when a file is
created and never modify or look at it after that.
*/
uint8_t reservedNT;
/**
The granularity of the seconds part of creationTime is 2 seconds
so this field is a count of tenths of a second and its valid
value range is 0-199 inclusive. (WHG note - seems to be hundredths)
*/
uint8_t creationTimeTenths;
/** Time file was created. */
uint16_t creationTime;
/** Date file was created. */
uint16_t creationDate;
/**
Last access date. Note that there is no last access time, only
a date. This is the date of last read or write. In the case of
a write, this should be set to the same date as lastWriteDate.
*/
uint16_t lastAccessDate;
/**
High word of this entry's first cluster number (always 0 for a
FAT12 or FAT16 volume).
*/
uint16_t firstClusterHigh;
/** Time of last write. File creation is considered a write. */
uint16_t lastWriteTime;
/** Date of last write. File creation is considered a write. */
uint16_t lastWriteDate;
/** Low word of this entry's first cluster number. */
uint16_t firstClusterLow;
/** 32-bit unsigned holding this file's size in bytes. */
uint32_t fileSize;
} __attribute__((packed));
//------------------------------------------------------------------------------
// Definitions for directory entries
//
/** Type name for directoryEntry */
typedef struct directoryEntry dir_t;
/** escape for name[0] = 0XE5 */
uint8_t const DIR_NAME_0XE5 = 0X05;
/** name[0] value for entry that is free after being "deleted" */
uint8_t const DIR_NAME_DELETED = 0XE5;
/** name[0] value for entry that is free and no allocated entries follow */
uint8_t const DIR_NAME_FREE = 0X00;
/** file is read-only */
uint8_t const DIR_ATT_READ_ONLY = 0X01;
/** File should hidden in directory listings */
uint8_t const DIR_ATT_HIDDEN = 0X02;
/** Entry is for a system file */
uint8_t const DIR_ATT_SYSTEM = 0X04;
/** Directory entry contains the volume label */
uint8_t const DIR_ATT_VOLUME_ID = 0X08;
/** Entry is for a directory */
uint8_t const DIR_ATT_DIRECTORY = 0X10;
/** Old DOS archive bit for backup support */
uint8_t const DIR_ATT_ARCHIVE = 0X20;
/** Test value for long name entry. Test is
(d->attributes & DIR_ATT_LONG_NAME_MASK) == DIR_ATT_LONG_NAME. */
uint8_t const DIR_ATT_LONG_NAME = 0X0F;
/** Test mask for long name entry */
uint8_t const DIR_ATT_LONG_NAME_MASK = 0X3F;
/** defined attribute bits */
uint8_t const DIR_ATT_DEFINED_BITS = 0X3F;
/** Directory entry is part of a long name */
static inline uint8_t DIR_IS_LONG_NAME(const dir_t* dir) {
return (dir->attributes & DIR_ATT_LONG_NAME_MASK) == DIR_ATT_LONG_NAME;
}
/** Mask for file/subdirectory tests */
uint8_t const DIR_ATT_FILE_TYPE_MASK = (DIR_ATT_VOLUME_ID | DIR_ATT_DIRECTORY);
/** Directory entry is for a file */
static inline uint8_t DIR_IS_FILE(const dir_t* dir) {
return (dir->attributes & DIR_ATT_FILE_TYPE_MASK) == 0;
}
/** Directory entry is for a subdirectory */
static inline uint8_t DIR_IS_SUBDIR(const dir_t* dir) {
return (dir->attributes & DIR_ATT_FILE_TYPE_MASK) == DIR_ATT_DIRECTORY;
}
/** Directory entry is for a file or subdirectory */
static inline uint8_t DIR_IS_FILE_OR_SUBDIR(const dir_t* dir) {
return (dir->attributes & DIR_ATT_VOLUME_ID) == 0;
}
#endif // FatStructs_h

View File

@ -0,0 +1,777 @@
/* Arduino Sd2Card Library
Copyright (C) 2009 by William Greiman
This file is part of the Arduino Sd2Card Library
This Library is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with the Arduino Sd2Card Library. If not, see
<http://www.gnu.org/licenses/>.
*/
#define USE_SPI_LIB
#include <Arduino.h>
#include "Sd2Card.h"
//------------------------------------------------------------------------------
#ifndef SOFTWARE_SPI
#ifdef USE_SPI_LIB
#ifndef SDCARD_SPI
#define SDCARD_SPI SPI
#endif
#include <SPI.h>
static SPISettings settings;
#endif
// functions for hardware SPI
/** Send a byte to the card */
static void spiSend(uint8_t b) {
#ifndef USE_SPI_LIB
SPDR = b;
while (!(SPSR & (1 << SPIF)))
;
#else
SDCARD_SPI.transfer(b);
#endif
}
/** Receive a byte from the card */
static uint8_t spiRec(void) {
#ifndef USE_SPI_LIB
spiSend(0XFF);
return SPDR;
#else
return SDCARD_SPI.transfer(0xFF);
#endif
}
#else // SOFTWARE_SPI
//------------------------------------------------------------------------------
/** nop to tune soft SPI timing */
#define nop asm volatile ("nop\n\t")
//------------------------------------------------------------------------------
/** Soft SPI receive */
uint8_t spiRec(void) {
uint8_t data = 0;
// no interrupts during byte receive - about 8 us
cli();
// output pin high - like sending 0XFF
fastDigitalWrite(SPI_MOSI_PIN, HIGH);
for (uint8_t i = 0; i < 8; i++) {
fastDigitalWrite(SPI_SCK_PIN, HIGH);
// adjust so SCK is nice
nop;
nop;
data <<= 1;
if (fastDigitalRead(SPI_MISO_PIN)) {
data |= 1;
}
fastDigitalWrite(SPI_SCK_PIN, LOW);
}
// enable interrupts
sei();
return data;
}
//------------------------------------------------------------------------------
/** Soft SPI send */
void spiSend(uint8_t data) {
// no interrupts during byte send - about 8 us
cli();
for (uint8_t i = 0; i < 8; i++) {
fastDigitalWrite(SPI_SCK_PIN, LOW);
fastDigitalWrite(SPI_MOSI_PIN, data & 0X80);
data <<= 1;
fastDigitalWrite(SPI_SCK_PIN, HIGH);
}
// hold SCK high for a few ns
nop;
nop;
nop;
nop;
fastDigitalWrite(SPI_SCK_PIN, LOW);
// enable interrupts
sei();
}
#endif // SOFTWARE_SPI
//------------------------------------------------------------------------------
// send command and return error code. Return zero for OK
uint8_t Sd2Card::cardCommand(uint8_t cmd, uint32_t arg) {
// end read if in partialBlockRead mode
readEnd();
// select card
chipSelectLow();
// wait up to 300 ms if busy
waitNotBusy(300);
// send command
spiSend(cmd | 0x40);
// send argument
for (int8_t s = 24; s >= 0; s -= 8) {
spiSend(arg >> s);
}
// send CRC
uint8_t crc = 0XFF;
if (cmd == CMD0) {
crc = 0X95; // correct crc for CMD0 with arg 0
}
if (cmd == CMD8) {
crc = 0X87; // correct crc for CMD8 with arg 0X1AA
}
spiSend(crc);
// wait for response
for (uint8_t i = 0; ((status_ = spiRec()) & 0X80) && i != 0XFF; i++)
;
return status_;
}
//------------------------------------------------------------------------------
/**
Determine the size of an SD flash memory card.
\return The number of 512 byte data blocks in the card
or zero if an error occurs.
*/
uint32_t Sd2Card::cardSize(void) {
csd_t csd;
if (!readCSD(&csd)) {
return 0;
}
if (csd.v1.csd_ver == 0) {
uint8_t read_bl_len = csd.v1.read_bl_len;
uint16_t c_size = (csd.v1.c_size_high << 10)
| (csd.v1.c_size_mid << 2) | csd.v1.c_size_low;
uint8_t c_size_mult = (csd.v1.c_size_mult_high << 1)
| csd.v1.c_size_mult_low;
return (uint32_t)(c_size + 1) << (c_size_mult + read_bl_len - 7);
} else if (csd.v2.csd_ver == 1) {
uint32_t c_size = ((uint32_t)csd.v2.c_size_high << 16)
| (csd.v2.c_size_mid << 8) | csd.v2.c_size_low;
return (c_size + 1) << 10;
} else {
error(SD_CARD_ERROR_BAD_CSD);
return 0;
}
}
//------------------------------------------------------------------------------
static uint8_t chip_select_asserted = 0;
void Sd2Card::chipSelectHigh(void) {
digitalWrite(chipSelectPin_, HIGH);
#ifdef USE_SPI_LIB
if (chip_select_asserted) {
chip_select_asserted = 0;
SDCARD_SPI.endTransaction();
}
#endif
}
//------------------------------------------------------------------------------
void Sd2Card::chipSelectLow(void) {
#ifdef USE_SPI_LIB
if (!chip_select_asserted) {
chip_select_asserted = 1;
SDCARD_SPI.beginTransaction(settings);
}
#endif
digitalWrite(chipSelectPin_, LOW);
}
//------------------------------------------------------------------------------
/** Erase a range of blocks.
\param[in] firstBlock The address of the first block in the range.
\param[in] lastBlock The address of the last block in the range.
\note This function requests the SD card to do a flash erase for a
range of blocks. The data on the card after an erase operation is
either 0 or 1, depends on the card vendor. The card must support
single block erase.
\return The value one, true, is returned for success and
the value zero, false, is returned for failure.
*/
uint8_t Sd2Card::erase(uint32_t firstBlock, uint32_t lastBlock) {
if (!eraseSingleBlockEnable()) {
error(SD_CARD_ERROR_ERASE_SINGLE_BLOCK);
goto fail;
}
if (type_ != SD_CARD_TYPE_SDHC) {
firstBlock <<= 9;
lastBlock <<= 9;
}
if (cardCommand(CMD32, firstBlock)
|| cardCommand(CMD33, lastBlock)
|| cardCommand(CMD38, 0)) {
error(SD_CARD_ERROR_ERASE);
goto fail;
}
if (!waitNotBusy(SD_ERASE_TIMEOUT)) {
error(SD_CARD_ERROR_ERASE_TIMEOUT);
goto fail;
}
chipSelectHigh();
return true;
fail:
chipSelectHigh();
return false;
}
//------------------------------------------------------------------------------
/** Determine if card supports single block erase.
\return The value one, true, is returned if single block erase is supported.
The value zero, false, is returned if single block erase is not supported.
*/
uint8_t Sd2Card::eraseSingleBlockEnable(void) {
csd_t csd;
return readCSD(&csd) ? csd.v1.erase_blk_en : 0;
}
//------------------------------------------------------------------------------
/**
Initialize an SD flash memory card.
\param[in] sckRateID SPI clock rate selector. See setSckRate().
\param[in] chipSelectPin SD chip select pin number.
\return The value one, true, is returned for success and
the value zero, false, is returned for failure. The reason for failure
can be determined by calling errorCode() and errorData().
*/
uint8_t Sd2Card::init(uint8_t sckRateID, uint8_t chipSelectPin) {
errorCode_ = inBlock_ = partialBlockRead_ = type_ = 0;
chipSelectPin_ = chipSelectPin;
// 16-bit init start time allows over a minute
unsigned int t0 = millis();
uint32_t arg;
// set pin modes
pinMode(chipSelectPin_, OUTPUT);
digitalWrite(chipSelectPin_, HIGH);
#ifndef USE_SPI_LIB
pinMode(SPI_MISO_PIN, INPUT);
pinMode(SPI_MOSI_PIN, OUTPUT);
pinMode(SPI_SCK_PIN, OUTPUT);
#endif
#ifndef SOFTWARE_SPI
#ifndef USE_SPI_LIB
// SS must be in output mode even it is not chip select
pinMode(SS_PIN, OUTPUT);
digitalWrite(SS_PIN, HIGH); // disable any SPI device using hardware SS pin
// Enable SPI, Master, clock rate f_osc/128
SPCR = (1 << SPE) | (1 << MSTR) | (1 << SPR1) | (1 << SPR0);
// clear double speed
SPSR &= ~(1 << SPI2X);
#else // USE_SPI_LIB
SDCARD_SPI.begin();
settings = SPISettings(250000, MSBFIRST, SPI_MODE0);
#endif // USE_SPI_LIB
#endif // SOFTWARE_SPI
// must supply min of 74 clock cycles with CS high.
#ifdef USE_SPI_LIB
SDCARD_SPI.beginTransaction(settings);
#endif
for (uint8_t i = 0; i < 10; i++) {
spiSend(0XFF);
}
#ifdef USE_SPI_LIB
SDCARD_SPI.endTransaction();
#endif
chipSelectLow();
// command to go idle in SPI mode
while ((status_ = cardCommand(CMD0, 0)) != R1_IDLE_STATE) {
unsigned int d = millis() - t0;
if (d > SD_INIT_TIMEOUT) {
error(SD_CARD_ERROR_CMD0);
goto fail;
}
}
// check SD version
if ((cardCommand(CMD8, 0x1AA) & R1_ILLEGAL_COMMAND)) {
type(SD_CARD_TYPE_SD1);
} else {
// only need last byte of r7 response
for (uint8_t i = 0; i < 4; i++) {
status_ = spiRec();
}
if (status_ != 0XAA) {
error(SD_CARD_ERROR_CMD8);
goto fail;
}
type(SD_CARD_TYPE_SD2);
}
// initialize card and send host supports SDHC if SD2
arg = type() == SD_CARD_TYPE_SD2 ? 0X40000000 : 0;
while ((status_ = cardAcmd(ACMD41, arg)) != R1_READY_STATE) {
// check for timeout
unsigned int d = millis() - t0;
if (d > SD_INIT_TIMEOUT) {
error(SD_CARD_ERROR_ACMD41);
goto fail;
}
}
// if SD2 read OCR register to check for SDHC card
if (type() == SD_CARD_TYPE_SD2) {
if (cardCommand(CMD58, 0)) {
error(SD_CARD_ERROR_CMD58);
goto fail;
}
if ((spiRec() & 0XC0) == 0XC0) {
type(SD_CARD_TYPE_SDHC);
}
// discard rest of ocr - contains allowed voltage range
for (uint8_t i = 0; i < 3; i++) {
spiRec();
}
}
chipSelectHigh();
#ifndef SOFTWARE_SPI
return setSckRate(sckRateID);
#else // SOFTWARE_SPI
return true;
#endif // SOFTWARE_SPI
fail:
chipSelectHigh();
return false;
}
//------------------------------------------------------------------------------
/**
Enable or disable partial block reads.
Enabling partial block reads improves performance by allowing a block
to be read over the SPI bus as several sub-blocks. Errors may occur
if the time between reads is too long since the SD card may timeout.
The SPI SS line will be held low until the entire block is read or
readEnd() is called.
Use this for applications like the Adafruit Wave Shield.
\param[in] value The value TRUE (non-zero) or FALSE (zero).)
*/
void Sd2Card::partialBlockRead(uint8_t value) {
readEnd();
partialBlockRead_ = value;
}
//------------------------------------------------------------------------------
/**
Read a 512 byte block from an SD card device.
\param[in] block Logical block to be read.
\param[out] dst Pointer to the location that will receive the data.
\return The value one, true, is returned for success and
the value zero, false, is returned for failure.
*/
uint8_t Sd2Card::readBlock(uint32_t block, uint8_t* dst) {
return readData(block, 0, 512, dst);
}
//------------------------------------------------------------------------------
/**
Read part of a 512 byte block from an SD card.
\param[in] block Logical block to be read.
\param[in] offset Number of bytes to skip at start of block
\param[out] dst Pointer to the location that will receive the data.
\param[in] count Number of bytes to read
\return The value one, true, is returned for success and
the value zero, false, is returned for failure.
*/
uint8_t Sd2Card::readData(uint32_t block,
uint16_t offset, uint16_t count, uint8_t* dst) {
if (count == 0) {
return true;
}
if ((count + offset) > 512) {
goto fail;
}
if (!inBlock_ || block != block_ || offset < offset_) {
block_ = block;
// use address if not SDHC card
if (type() != SD_CARD_TYPE_SDHC) {
block <<= 9;
}
if (cardCommand(CMD17, block)) {
error(SD_CARD_ERROR_CMD17);
goto fail;
}
if (!waitStartBlock()) {
goto fail;
}
offset_ = 0;
inBlock_ = 1;
}
#ifdef OPTIMIZE_HARDWARE_SPI
// start first spi transfer
SPDR = 0XFF;
// skip data before offset
for (; offset_ < offset; offset_++) {
while (!(SPSR & (1 << SPIF)))
;
SPDR = 0XFF;
}
// transfer data
n = count - 1;
for (uint16_t i = 0; i < n; i++) {
while (!(SPSR & (1 << SPIF)))
;
dst[i] = SPDR;
SPDR = 0XFF;
}
// wait for last byte
while (!(SPSR & (1 << SPIF)))
;
dst[n] = SPDR;
#else // OPTIMIZE_HARDWARE_SPI
// skip data before offset
for (; offset_ < offset; offset_++) {
spiRec();
}
// transfer data
for (uint16_t i = 0; i < count; i++) {
dst[i] = spiRec();
}
#endif // OPTIMIZE_HARDWARE_SPI
offset_ += count;
if (!partialBlockRead_ || offset_ >= 512) {
// read rest of data, checksum and set chip select high
readEnd();
}
return true;
fail:
chipSelectHigh();
return false;
}
//------------------------------------------------------------------------------
/** Skip remaining data in a block when in partial block read mode. */
void Sd2Card::readEnd(void) {
if (inBlock_) {
// skip data and crc
#ifdef OPTIMIZE_HARDWARE_SPI
// optimize skip for hardware
SPDR = 0XFF;
while (offset_++ < 513) {
while (!(SPSR & (1 << SPIF)))
;
SPDR = 0XFF;
}
// wait for last crc byte
while (!(SPSR & (1 << SPIF)))
;
#else // OPTIMIZE_HARDWARE_SPI
while (offset_++ < 514) {
spiRec();
}
#endif // OPTIMIZE_HARDWARE_SPI
chipSelectHigh();
inBlock_ = 0;
}
}
//------------------------------------------------------------------------------
/** read CID or CSR register */
uint8_t Sd2Card::readRegister(uint8_t cmd, void* buf) {
uint8_t* dst = reinterpret_cast<uint8_t*>(buf);
if (cardCommand(cmd, 0)) {
error(SD_CARD_ERROR_READ_REG);
goto fail;
}
if (!waitStartBlock()) {
goto fail;
}
// transfer data
for (uint16_t i = 0; i < 16; i++) {
dst[i] = spiRec();
}
spiRec(); // get first crc byte
spiRec(); // get second crc byte
chipSelectHigh();
return true;
fail:
chipSelectHigh();
return false;
}
//------------------------------------------------------------------------------
/**
Set the SPI clock rate.
\param[in] sckRateID A value in the range [0, 6].
The SPI clock will be set to F_CPU/pow(2, 1 + sckRateID). The maximum
SPI rate is F_CPU/2 for \a sckRateID = 0 and the minimum rate is F_CPU/128
for \a scsRateID = 6.
\return The value one, true, is returned for success and the value zero,
false, is returned for an invalid value of \a sckRateID.
*/
uint8_t Sd2Card::setSckRate(uint8_t sckRateID) {
if (sckRateID > 6) {
error(SD_CARD_ERROR_SCK_RATE);
return false;
}
#ifndef USE_SPI_LIB
// see avr processor datasheet for SPI register bit definitions
if ((sckRateID & 1) || sckRateID == 6) {
SPSR &= ~(1 << SPI2X);
} else {
SPSR |= (1 << SPI2X);
}
SPCR &= ~((1 << SPR1) | (1 << SPR0));
SPCR |= (sckRateID & 4 ? (1 << SPR1) : 0)
| (sckRateID & 2 ? (1 << SPR0) : 0);
#else // USE_SPI_LIB
switch (sckRateID) {
case 0: settings = SPISettings(25000000, MSBFIRST, SPI_MODE0); break;
case 1: settings = SPISettings(4000000, MSBFIRST, SPI_MODE0); break;
case 2: settings = SPISettings(2000000, MSBFIRST, SPI_MODE0); break;
case 3: settings = SPISettings(1000000, MSBFIRST, SPI_MODE0); break;
case 4: settings = SPISettings(500000, MSBFIRST, SPI_MODE0); break;
case 5: settings = SPISettings(250000, MSBFIRST, SPI_MODE0); break;
default: settings = SPISettings(125000, MSBFIRST, SPI_MODE0);
}
#endif // USE_SPI_LIB
return true;
}
#ifdef USE_SPI_LIB
//------------------------------------------------------------------------------
// set the SPI clock frequency
uint8_t Sd2Card::setSpiClock(uint32_t clock) {
settings = SPISettings(clock, MSBFIRST, SPI_MODE0);
return true;
}
#endif
//------------------------------------------------------------------------------
// wait for card to go not busy
uint8_t Sd2Card::waitNotBusy(unsigned int timeoutMillis) {
unsigned int t0 = millis();
unsigned int d;
do {
if (spiRec() == 0XFF) {
return true;
}
d = millis() - t0;
} while (d < timeoutMillis);
return false;
}
//------------------------------------------------------------------------------
/** Wait for start block token */
uint8_t Sd2Card::waitStartBlock(void) {
unsigned int t0 = millis();
while ((status_ = spiRec()) == 0XFF) {
unsigned int d = millis() - t0;
if (d > SD_READ_TIMEOUT) {
error(SD_CARD_ERROR_READ_TIMEOUT);
goto fail;
}
}
if (status_ != DATA_START_BLOCK) {
error(SD_CARD_ERROR_READ);
goto fail;
}
return true;
fail:
chipSelectHigh();
return false;
}
//------------------------------------------------------------------------------
/**
Writes a 512 byte block to an SD card.
\param[in] blockNumber Logical block to be written.
\param[in] src Pointer to the location of the data to be written.
\param[in] blocking If the write should be blocking.
\return The value one, true, is returned for success and
the value zero, false, is returned for failure.
*/
uint8_t Sd2Card::writeBlock(uint32_t blockNumber, const uint8_t* src, uint8_t blocking) {
#if SD_PROTECT_BLOCK_ZERO
// don't allow write to first block
if (blockNumber == 0) {
error(SD_CARD_ERROR_WRITE_BLOCK_ZERO);
goto fail;
}
#endif // SD_PROTECT_BLOCK_ZERO
// use address if not SDHC card
if (type() != SD_CARD_TYPE_SDHC) {
blockNumber <<= 9;
}
if (cardCommand(CMD24, blockNumber)) {
error(SD_CARD_ERROR_CMD24);
goto fail;
}
if (!writeData(DATA_START_BLOCK, src)) {
goto fail;
}
if (blocking) {
// wait for flash programming to complete
if (!waitNotBusy(SD_WRITE_TIMEOUT)) {
error(SD_CARD_ERROR_WRITE_TIMEOUT);
goto fail;
}
// response is r2 so get and check two bytes for nonzero
if (cardCommand(CMD13, 0) || spiRec()) {
error(SD_CARD_ERROR_WRITE_PROGRAMMING);
goto fail;
}
}
chipSelectHigh();
return true;
fail:
chipSelectHigh();
return false;
}
//------------------------------------------------------------------------------
/** Write one data block in a multiple block write sequence */
uint8_t Sd2Card::writeData(const uint8_t* src) {
// wait for previous write to finish
if (!waitNotBusy(SD_WRITE_TIMEOUT)) {
error(SD_CARD_ERROR_WRITE_MULTIPLE);
chipSelectHigh();
return false;
}
return writeData(WRITE_MULTIPLE_TOKEN, src);
}
//------------------------------------------------------------------------------
// send one block of data for write block or write multiple blocks
uint8_t Sd2Card::writeData(uint8_t token, const uint8_t* src) {
#ifdef OPTIMIZE_HARDWARE_SPI
// send data - optimized loop
SPDR = token;
// send two byte per iteration
for (uint16_t i = 0; i < 512; i += 2) {
while (!(SPSR & (1 << SPIF)))
;
SPDR = src[i];
while (!(SPSR & (1 << SPIF)))
;
SPDR = src[i + 1];
}
// wait for last data byte
while (!(SPSR & (1 << SPIF)))
;
#else // OPTIMIZE_HARDWARE_SPI
spiSend(token);
for (uint16_t i = 0; i < 512; i++) {
spiSend(src[i]);
}
#endif // OPTIMIZE_HARDWARE_SPI
spiSend(0xff); // dummy crc
spiSend(0xff); // dummy crc
status_ = spiRec();
if ((status_ & DATA_RES_MASK) != DATA_RES_ACCEPTED) {
error(SD_CARD_ERROR_WRITE);
chipSelectHigh();
return false;
}
return true;
}
//------------------------------------------------------------------------------
/** Start a write multiple blocks sequence.
\param[in] blockNumber Address of first block in sequence.
\param[in] eraseCount The number of blocks to be pre-erased.
\note This function is used with writeData() and writeStop()
for optimized multiple block writes.
\return The value one, true, is returned for success and
the value zero, false, is returned for failure.
*/
uint8_t Sd2Card::writeStart(uint32_t blockNumber, uint32_t eraseCount) {
#if SD_PROTECT_BLOCK_ZERO
// don't allow write to first block
if (blockNumber == 0) {
error(SD_CARD_ERROR_WRITE_BLOCK_ZERO);
goto fail;
}
#endif // SD_PROTECT_BLOCK_ZERO
// send pre-erase count
if (cardAcmd(ACMD23, eraseCount)) {
error(SD_CARD_ERROR_ACMD23);
goto fail;
}
// use address if not SDHC card
if (type() != SD_CARD_TYPE_SDHC) {
blockNumber <<= 9;
}
if (cardCommand(CMD25, blockNumber)) {
error(SD_CARD_ERROR_CMD25);
goto fail;
}
return true;
fail:
chipSelectHigh();
return false;
}
//------------------------------------------------------------------------------
/** End a write multiple blocks sequence.
\return The value one, true, is returned for success and
the value zero, false, is returned for failure.
*/
uint8_t Sd2Card::writeStop(void) {
if (!waitNotBusy(SD_WRITE_TIMEOUT)) {
goto fail;
}
spiSend(STOP_TRAN_TOKEN);
if (!waitNotBusy(SD_WRITE_TIMEOUT)) {
goto fail;
}
chipSelectHigh();
return true;
fail:
error(SD_CARD_ERROR_STOP_TRAN);
chipSelectHigh();
return false;
}
//------------------------------------------------------------------------------
/** Check if the SD card is busy
\return The value one, true, is returned when is busy and
the value zero, false, is returned for when is NOT busy.
*/
uint8_t Sd2Card::isBusy(void) {
chipSelectLow();
byte b = spiRec();
chipSelectHigh();
return (b != 0XFF);
}

View File

@ -0,0 +1,273 @@
/* Arduino Sd2Card Library
Copyright (C) 2009 by William Greiman
This file is part of the Arduino Sd2Card Library
This Library is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with the Arduino Sd2Card Library. If not, see
<http://www.gnu.org/licenses/>.
*/
#ifndef Sd2Card_h
#define Sd2Card_h
/**
\file
Sd2Card class
*/
#include "Sd2PinMap.h"
#include "SdInfo.h"
/** Set SCK to max rate of F_CPU/2. See Sd2Card::setSckRate(). */
uint8_t const SPI_FULL_SPEED = 0;
/** Set SCK rate to F_CPU/4. See Sd2Card::setSckRate(). */
uint8_t const SPI_HALF_SPEED = 1;
/** Set SCK rate to F_CPU/8. Sd2Card::setSckRate(). */
uint8_t const SPI_QUARTER_SPEED = 2;
/**
USE_SPI_LIB: if set, use the SPI library bundled with Arduino IDE, otherwise
run with a standalone driver for AVR.
*/
#define USE_SPI_LIB
/**
Define MEGA_SOFT_SPI non-zero to use software SPI on Mega Arduinos.
Pins used are SS 10, MOSI 11, MISO 12, and SCK 13.
MEGA_SOFT_SPI allows an unmodified Adafruit GPS Shield to be used
on Mega Arduinos. Software SPI works well with GPS Shield V1.1
but many SD cards will fail with GPS Shield V1.0.
*/
#define MEGA_SOFT_SPI 0
//------------------------------------------------------------------------------
#if MEGA_SOFT_SPI && (defined(__AVR_ATmega1280__)||defined(__AVR_ATmega2560__))
#define SOFTWARE_SPI
#endif // MEGA_SOFT_SPI
//------------------------------------------------------------------------------
// SPI pin definitions
//
#ifndef SOFTWARE_SPI
// hardware pin defs
// include pins_arduino.h or variant.h depending on architecture, via Arduino.h
#include <Arduino.h>
/**
SD Chip Select pin
Warning if this pin is redefined the hardware SS will pin will be enabled
as an output by init(). An avr processor will not function as an SPI
master unless SS is set to output mode.
*/
#ifndef SDCARD_SS_PIN
/** The default chip select pin for the SD card is SS. */
uint8_t const SD_CHIP_SELECT_PIN = SS;
#else
uint8_t const SD_CHIP_SELECT_PIN = SDCARD_SS_PIN;
#endif
// The following three pins must not be redefined for hardware SPI,
// so ensure that they are taken from pins_arduino.h or variant.h, depending on architecture.
#ifndef SDCARD_MOSI_PIN
/** SPI Master Out Slave In pin */
uint8_t const SPI_MOSI_PIN = MOSI;
/** SPI Master In Slave Out pin */
uint8_t const SPI_MISO_PIN = MISO;
/** SPI Clock pin */
uint8_t const SPI_SCK_PIN = SCK;
#else
uint8_t const SPI_MOSI_PIN = SDCARD_MOSI_PIN;
uint8_t const SPI_MISO_PIN = SDCARD_MISO_PIN;
uint8_t const SPI_SCK_PIN = SDCARD_SCK_PIN;
#endif
/** optimize loops for hardware SPI */
#ifndef USE_SPI_LIB
#define OPTIMIZE_HARDWARE_SPI
#endif
#else // SOFTWARE_SPI
// define software SPI pins so Mega can use unmodified GPS Shield
/** SPI chip select pin */
uint8_t const SD_CHIP_SELECT_PIN = 10;
/** SPI Master Out Slave In pin */
uint8_t const SPI_MOSI_PIN = 11;
/** SPI Master In Slave Out pin */
uint8_t const SPI_MISO_PIN = 12;
/** SPI Clock pin */
uint8_t const SPI_SCK_PIN = 13;
#endif // SOFTWARE_SPI
//------------------------------------------------------------------------------
/** Protect block zero from write if nonzero */
#define SD_PROTECT_BLOCK_ZERO 1
/** init timeout ms */
unsigned int const SD_INIT_TIMEOUT = 2000;
/** erase timeout ms */
unsigned int const SD_ERASE_TIMEOUT = 10000;
/** read timeout ms */
unsigned int const SD_READ_TIMEOUT = 300;
/** write time out ms */
unsigned int const SD_WRITE_TIMEOUT = 600;
//------------------------------------------------------------------------------
// SD card errors
/** timeout error for command CMD0 */
uint8_t const SD_CARD_ERROR_CMD0 = 0X1;
/** CMD8 was not accepted - not a valid SD card*/
uint8_t const SD_CARD_ERROR_CMD8 = 0X2;
/** card returned an error response for CMD17 (read block) */
uint8_t const SD_CARD_ERROR_CMD17 = 0X3;
/** card returned an error response for CMD24 (write block) */
uint8_t const SD_CARD_ERROR_CMD24 = 0X4;
/** WRITE_MULTIPLE_BLOCKS command failed */
uint8_t const SD_CARD_ERROR_CMD25 = 0X05;
/** card returned an error response for CMD58 (read OCR) */
uint8_t const SD_CARD_ERROR_CMD58 = 0X06;
/** SET_WR_BLK_ERASE_COUNT failed */
uint8_t const SD_CARD_ERROR_ACMD23 = 0X07;
/** card's ACMD41 initialization process timeout */
uint8_t const SD_CARD_ERROR_ACMD41 = 0X08;
/** card returned a bad CSR version field */
uint8_t const SD_CARD_ERROR_BAD_CSD = 0X09;
/** erase block group command failed */
uint8_t const SD_CARD_ERROR_ERASE = 0X0A;
/** card not capable of single block erase */
uint8_t const SD_CARD_ERROR_ERASE_SINGLE_BLOCK = 0X0B;
/** Erase sequence timed out */
uint8_t const SD_CARD_ERROR_ERASE_TIMEOUT = 0X0C;
/** card returned an error token instead of read data */
uint8_t const SD_CARD_ERROR_READ = 0X0D;
/** read CID or CSD failed */
uint8_t const SD_CARD_ERROR_READ_REG = 0X0E;
/** timeout while waiting for start of read data */
uint8_t const SD_CARD_ERROR_READ_TIMEOUT = 0X0F;
/** card did not accept STOP_TRAN_TOKEN */
uint8_t const SD_CARD_ERROR_STOP_TRAN = 0X10;
/** card returned an error token as a response to a write operation */
uint8_t const SD_CARD_ERROR_WRITE = 0X11;
/** attempt to write protected block zero */
uint8_t const SD_CARD_ERROR_WRITE_BLOCK_ZERO = 0X12;
/** card did not go ready for a multiple block write */
uint8_t const SD_CARD_ERROR_WRITE_MULTIPLE = 0X13;
/** card returned an error to a CMD13 status check after a write */
uint8_t const SD_CARD_ERROR_WRITE_PROGRAMMING = 0X14;
/** timeout occurred during write programming */
uint8_t const SD_CARD_ERROR_WRITE_TIMEOUT = 0X15;
/** incorrect rate selected */
uint8_t const SD_CARD_ERROR_SCK_RATE = 0X16;
//------------------------------------------------------------------------------
// card types
/** Standard capacity V1 SD card */
uint8_t const SD_CARD_TYPE_SD1 = 1;
/** Standard capacity V2 SD card */
uint8_t const SD_CARD_TYPE_SD2 = 2;
/** High Capacity SD card */
uint8_t const SD_CARD_TYPE_SDHC = 3;
//------------------------------------------------------------------------------
/**
\class Sd2Card
\brief Raw access to SD and SDHC flash memory cards.
*/
class Sd2Card {
public:
/** Construct an instance of Sd2Card. */
Sd2Card(void) : errorCode_(0), inBlock_(0), partialBlockRead_(0), type_(0) {}
uint32_t cardSize(void);
uint8_t erase(uint32_t firstBlock, uint32_t lastBlock);
uint8_t eraseSingleBlockEnable(void);
/**
\return error code for last error. See Sd2Card.h for a list of error codes.
*/
uint8_t errorCode(void) const {
return errorCode_;
}
/** \return error data for last error. */
uint8_t errorData(void) const {
return status_;
}
/**
Initialize an SD flash memory card with default clock rate and chip
select pin. See sd2Card::init(uint8_t sckRateID, uint8_t chipSelectPin).
*/
uint8_t init(void) {
return init(SPI_FULL_SPEED, SD_CHIP_SELECT_PIN);
}
/**
Initialize an SD flash memory card with the selected SPI clock rate
and the default SD chip select pin.
See sd2Card::init(uint8_t sckRateID, uint8_t chipSelectPin).
*/
uint8_t init(uint8_t sckRateID) {
return init(sckRateID, SD_CHIP_SELECT_PIN);
}
uint8_t init(uint8_t sckRateID, uint8_t chipSelectPin);
void partialBlockRead(uint8_t value);
/** Returns the current value, true or false, for partial block read. */
uint8_t partialBlockRead(void) const {
return partialBlockRead_;
}
uint8_t readBlock(uint32_t block, uint8_t* dst);
uint8_t readData(uint32_t block,
uint16_t offset, uint16_t count, uint8_t* dst);
/**
Read a cards CID register. The CID contains card identification
information such as Manufacturer ID, Product name, Product serial
number and Manufacturing date. */
uint8_t readCID(cid_t* cid) {
return readRegister(CMD10, cid);
}
/**
Read a cards CSD register. The CSD contains Card-Specific Data that
provides information regarding access to the card's contents. */
uint8_t readCSD(csd_t* csd) {
return readRegister(CMD9, csd);
}
void readEnd(void);
uint8_t setSckRate(uint8_t sckRateID);
#ifdef USE_SPI_LIB
uint8_t setSpiClock(uint32_t clock);
#endif
/** Return the card type: SD V1, SD V2 or SDHC */
uint8_t type(void) const {
return type_;
}
uint8_t writeBlock(uint32_t blockNumber, const uint8_t* src, uint8_t blocking = 1);
uint8_t writeData(const uint8_t* src);
uint8_t writeStart(uint32_t blockNumber, uint32_t eraseCount);
uint8_t writeStop(void);
uint8_t isBusy(void);
private:
uint32_t block_;
uint8_t chipSelectPin_;
uint8_t errorCode_;
uint8_t inBlock_;
uint16_t offset_;
uint8_t partialBlockRead_;
uint8_t status_;
uint8_t type_;
// private functions
uint8_t cardAcmd(uint8_t cmd, uint32_t arg) {
cardCommand(CMD55, 0);
return cardCommand(cmd, arg);
}
uint8_t cardCommand(uint8_t cmd, uint32_t arg);
void error(uint8_t code) {
errorCode_ = code;
}
uint8_t readRegister(uint8_t cmd, void* buf);
uint8_t sendWriteCommand(uint32_t blockNumber, uint32_t eraseCount);
void chipSelectHigh(void);
void chipSelectLow(void);
void type(uint8_t value) {
type_ = value;
}
uint8_t waitNotBusy(unsigned int timeoutMillis);
uint8_t writeData(uint8_t token, const uint8_t* src);
uint8_t waitStartBlock(void);
};
#endif // Sd2Card_h

View File

@ -0,0 +1,525 @@
/* Arduino SdFat Library
Copyright (C) 2010 by William Greiman
This file is part of the Arduino SdFat Library
This Library is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with the Arduino SdFat Library. If not, see
<http://www.gnu.org/licenses/>.
*/
#if defined(__arm__) // Arduino Due Board follows
#ifndef Sd2PinMap_h
#define Sd2PinMap_h
#include <Arduino.h>
uint8_t const SS_PIN = SS;
uint8_t const MOSI_PIN = MOSI;
uint8_t const MISO_PIN = MISO;
uint8_t const SCK_PIN = SCK;
#endif // Sd2PinMap_h
#elif defined(__AVR_ATmega4809__) // Arduino UNO WiFI Rev2 follows
#ifndef Sd2PinMap_h
#define Sd2PinMap_h
#include <Arduino.h>
uint8_t const SS_PIN = SS;
uint8_t const MOSI_PIN = MOSI;
uint8_t const MISO_PIN = MISO;
uint8_t const SCK_PIN = SCK;
#endif // Sd2PinMap_h
#elif defined(__AVR__) // Other AVR based Boards follows
// Warning this file was generated by a program.
#ifndef Sd2PinMap_h
#define Sd2PinMap_h
#include <avr/io.h>
//------------------------------------------------------------------------------
/** struct for mapping digital pins */
struct pin_map_t {
volatile uint8_t* ddr;
volatile uint8_t* pin;
volatile uint8_t* port;
uint8_t bit;
};
//------------------------------------------------------------------------------
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
// Mega
// Two Wire (aka I2C) ports
uint8_t const SDA_PIN = 20;
uint8_t const SCL_PIN = 21;
// SPI port
uint8_t const SS_PIN = 53;
uint8_t const MOSI_PIN = 51;
uint8_t const MISO_PIN = 50;
uint8_t const SCK_PIN = 52;
static const pin_map_t digitalPinMap[] = {
{&DDRE, &PINE, &PORTE, 0}, // E0 0
{&DDRE, &PINE, &PORTE, 1}, // E1 1
{&DDRE, &PINE, &PORTE, 4}, // E4 2
{&DDRE, &PINE, &PORTE, 5}, // E5 3
{&DDRG, &PING, &PORTG, 5}, // G5 4
{&DDRE, &PINE, &PORTE, 3}, // E3 5
{&DDRH, &PINH, &PORTH, 3}, // H3 6
{&DDRH, &PINH, &PORTH, 4}, // H4 7
{&DDRH, &PINH, &PORTH, 5}, // H5 8
{&DDRH, &PINH, &PORTH, 6}, // H6 9
{&DDRB, &PINB, &PORTB, 4}, // B4 10
{&DDRB, &PINB, &PORTB, 5}, // B5 11
{&DDRB, &PINB, &PORTB, 6}, // B6 12
{&DDRB, &PINB, &PORTB, 7}, // B7 13
{&DDRJ, &PINJ, &PORTJ, 1}, // J1 14
{&DDRJ, &PINJ, &PORTJ, 0}, // J0 15
{&DDRH, &PINH, &PORTH, 1}, // H1 16
{&DDRH, &PINH, &PORTH, 0}, // H0 17
{&DDRD, &PIND, &PORTD, 3}, // D3 18
{&DDRD, &PIND, &PORTD, 2}, // D2 19
{&DDRD, &PIND, &PORTD, 1}, // D1 20
{&DDRD, &PIND, &PORTD, 0}, // D0 21
{&DDRA, &PINA, &PORTA, 0}, // A0 22
{&DDRA, &PINA, &PORTA, 1}, // A1 23
{&DDRA, &PINA, &PORTA, 2}, // A2 24
{&DDRA, &PINA, &PORTA, 3}, // A3 25
{&DDRA, &PINA, &PORTA, 4}, // A4 26
{&DDRA, &PINA, &PORTA, 5}, // A5 27
{&DDRA, &PINA, &PORTA, 6}, // A6 28
{&DDRA, &PINA, &PORTA, 7}, // A7 29
{&DDRC, &PINC, &PORTC, 7}, // C7 30
{&DDRC, &PINC, &PORTC, 6}, // C6 31
{&DDRC, &PINC, &PORTC, 5}, // C5 32
{&DDRC, &PINC, &PORTC, 4}, // C4 33
{&DDRC, &PINC, &PORTC, 3}, // C3 34
{&DDRC, &PINC, &PORTC, 2}, // C2 35
{&DDRC, &PINC, &PORTC, 1}, // C1 36
{&DDRC, &PINC, &PORTC, 0}, // C0 37
{&DDRD, &PIND, &PORTD, 7}, // D7 38
{&DDRG, &PING, &PORTG, 2}, // G2 39
{&DDRG, &PING, &PORTG, 1}, // G1 40
{&DDRG, &PING, &PORTG, 0}, // G0 41
{&DDRL, &PINL, &PORTL, 7}, // L7 42
{&DDRL, &PINL, &PORTL, 6}, // L6 43
{&DDRL, &PINL, &PORTL, 5}, // L5 44
{&DDRL, &PINL, &PORTL, 4}, // L4 45
{&DDRL, &PINL, &PORTL, 3}, // L3 46
{&DDRL, &PINL, &PORTL, 2}, // L2 47
{&DDRL, &PINL, &PORTL, 1}, // L1 48
{&DDRL, &PINL, &PORTL, 0}, // L0 49
{&DDRB, &PINB, &PORTB, 3}, // B3 50
{&DDRB, &PINB, &PORTB, 2}, // B2 51
{&DDRB, &PINB, &PORTB, 1}, // B1 52
{&DDRB, &PINB, &PORTB, 0}, // B0 53
{&DDRF, &PINF, &PORTF, 0}, // F0 54
{&DDRF, &PINF, &PORTF, 1}, // F1 55
{&DDRF, &PINF, &PORTF, 2}, // F2 56
{&DDRF, &PINF, &PORTF, 3}, // F3 57
{&DDRF, &PINF, &PORTF, 4}, // F4 58
{&DDRF, &PINF, &PORTF, 5}, // F5 59
{&DDRF, &PINF, &PORTF, 6}, // F6 60
{&DDRF, &PINF, &PORTF, 7}, // F7 61
{&DDRK, &PINK, &PORTK, 0}, // K0 62
{&DDRK, &PINK, &PORTK, 1}, // K1 63
{&DDRK, &PINK, &PORTK, 2}, // K2 64
{&DDRK, &PINK, &PORTK, 3}, // K3 65
{&DDRK, &PINK, &PORTK, 4}, // K4 66
{&DDRK, &PINK, &PORTK, 5}, // K5 67
{&DDRK, &PINK, &PORTK, 6}, // K6 68
{&DDRK, &PINK, &PORTK, 7} // K7 69
};
//------------------------------------------------------------------------------
#elif (defined(__AVR_ATmega644P__) || defined(__AVR_ATmega1284P__)) && defined(CORE_MICRODUINO)
// Microduino Core+
// Two Wire (aka I2C) ports
uint8_t const SDA_PIN = 20;
uint8_t const SCL_PIN = 21;
// SPI port
uint8_t const SS_PIN = 10;
uint8_t const MOSI_PIN = 11;
uint8_t const MISO_PIN = 12;
uint8_t const SCK_PIN = 13;
static const pin_map_t digitalPinMap[] = {
{&DDRD, &PIND, &PORTD, 0}, // D0 PD0
{&DDRD, &PIND, &PORTD, 1}, // D1 PD1
{&DDRD, &PIND, &PORTD, 2}, // D2 PD2
{&DDRD, &PIND, &PORTD, 3}, // D3 PD3
{&DDRB, &PINB, &PORTB, 0}, // D4 PB0
{&DDRB, &PINB, &PORTB, 1}, // D5 PB1
{&DDRB, &PINB, &PORTB, 2}, // D6 PB2
{&DDRB, &PINB, &PORTB, 3}, // D7 PB3
{&DDRD, &PIND, &PORTD, 6}, // D8 PD6
{&DDRD, &PIND, &PORTD, 5}, // D9 PD5
{&DDRB, &PINB, &PORTB, 4}, // D10 PB4
{&DDRB, &PINB, &PORTB, 5}, // D11 PB5
{&DDRB, &PINB, &PORTB, 6}, // D12 PB6
{&DDRB, &PINB, &PORTB, 7}, // D13 PB7
{&DDRC, &PINC, &PORTC, 7}, // D14 PC7
{&DDRC, &PINC, &PORTC, 6}, // D15 PC6
{&DDRC, &PINC, &PORTC, 5}, // D16 PC5
{&DDRC, &PINC, &PORTC, 4}, // D17 PC4
{&DDRC, &PINC, &PORTC, 3}, // D18 PC3
{&DDRC, &PINC, &PORTC, 2}, // D19 PC2
{&DDRC, &PINC, &PORTC, 1}, // D20 PC1
{&DDRC, &PINC, &PORTC, 0}, // D21 PC0
{&DDRD, &PIND, &PORTD, 4}, // D22 PD4
{&DDRD, &PIND, &PORTD, 7}, // D23 PD7
{&DDRA, &PINA, &PORTA, 7}, // D24 PA7
{&DDRA, &PINA, &PORTA, 6}, // D25 PA6
{&DDRA, &PINA, &PORTA, 5}, // D26 PA5
{&DDRA, &PINA, &PORTA, 4}, // D27 PA4
{&DDRA, &PINA, &PORTA, 3}, // D28 PA3
{&DDRA, &PINA, &PORTA, 2}, // D29 PA2
{&DDRA, &PINA, &PORTA, 1}, // D30 PA1
{&DDRA, &PINA, &PORTA, 0} // D31 PA0
};
//------------------------------------------------------------------------------
#elif defined(__AVR_ATmega128RFA1__) && defined(CORE_MICRODUINO)
// Microduino Core RF
// Two Wire (aka I2C) ports
uint8_t const SDA_PIN = 18;
uint8_t const SCL_PIN = 19;
// SPI port
uint8_t const SS_PIN = 10;
uint8_t const MOSI_PIN = 11;
uint8_t const MISO_PIN = 12;
uint8_t const SCK_PIN = 13;
static const pin_map_t digitalPinMap[] = {
{&DDRD, &PINE, &PORTE, 0}, // D0 PE0
{&DDRD, &PINE, &PORTE, 1}, // D1 PE1
{&DDRD, &PIND, &PORTD, 2}, // D2 PD2
{&DDRD, &PIND, &PORTD, 3}, // D3 PD3
{&DDRB, &PINE, &PORTE, 3}, // D4 PE3
{&DDRB, &PINE, &PORTE, 4}, // D5 PE4
{&DDRB, &PINE, &PORTE, 5}, // D6 PE5
{&DDRB, &PINB, &PORTB, 7}, // D7 PB7
{&DDRD, &PINB, &PORTB, 6}, // D8 PB6
{&DDRD, &PINB, &PORTB, 5}, // D9 PB5
{&DDRB, &PINB, &PORTB, 4}, // D10 PB4
{&DDRB, &PINB, &PORTB, 2}, // D11 PB2
{&DDRB, &PINB, &PORTB, 3}, // D12 PB3
{&DDRB, &PINB, &PORTB, 1}, // D13 PB1
{&DDRF, &PINF, &PORTF, 7}, // D14 PF7
{&DDRF, &PINF, &PORTF, 6}, // D15 PF6
{&DDRF, &PINF, &PORTF, 5}, // D16 PF5
{&DDRF, &PINF, &PORTF, 4}, // D17 PF4
{&DDRD, &PIND, &PORTD, 1}, // D18 PD1
{&DDRD, &PIND, &PORTD, 0}, // D19 PD0
{&DDRF, &PINF, &PORTF, 3}, // D20 PF3
{&DDRF, &PINF, &PORTF, 2}, // D21 PF2
};
//------------------------------------------------------------------------------
#elif defined(__AVR_ATmega32U4__) && defined(CORE_MICRODUINO)
// Microduino Core USB
// Two Wire (aka I2C) ports
uint8_t const SDA_PIN = 18;
uint8_t const SCL_PIN = 19;
// SPI port
uint8_t const SS_PIN = 10;
uint8_t const MOSI_PIN = 11;
uint8_t const MISO_PIN = 12;
uint8_t const SCK_PIN = 13;
static const pin_map_t digitalPinMap[] = {
{&DDRD, &PIND, &PORTD, 2}, // D0 - PD2
{&DDRD, &PIND, &PORTD, 3}, // D1 - PD3
{&DDRE, &PINE, &PORTE, 6}, // D2 - PE6
{&DDRD, &PIND, &PORTD, 6}, // D3 - PD6
{&DDRD, &PIND, &PORTD, 7}, // D4 - PD7
{&DDRC, &PINC, &PORTC, 6}, // D5 - PC6
{&DDRC, &PINC, &PORTC, 7}, // D6 - PC7
{&DDRE, &PINE, &PORTE, 7}, // D7 - PE7
{&DDRB, &PINB, &PORTB, 6}, // D8 - PB6
{&DDRB, &PINB, &PORTB, 5}, // D9 - PB5
{&DDRB, &PINB, &PORTB, 0}, // D10 - PB0
{&DDRB, &PINB, &PORTB, 2}, // D11 - MOSI - PB2
{&DDRB, &PINB, &PORTB, 3}, // D12 -MISO - PB3
{&DDRB, &PINB, &PORTB, 1}, // D13 -SCK - PB1
{&DDRF, &PINF, &PORTF, 7}, // D14 - A0 - PF7
{&DDRF, &PINF, &PORTF, 6}, // D15 - A1 - PF6
{&DDRF, &PINF, &PORTF, 5}, // D16 - A2 - PF5
{&DDRF, &PINF, &PORTF, 4}, // D17 - A3 - PF4
{&DDRD, &PIND, &PORTD, 1}, // D18 - PD1
{&DDRD, &PIND, &PORTD, 0}, // D19 - PD0
{&DDRF, &PINF, &PORTF, 1}, // D20 - A6 - PF1
{&DDRF, &PINF, &PORTF, 0}, // D21 - A7 - PF0
};
//------------------------------------------------------------------------------
#elif defined(__AVR_ATmega644P__) || defined(__AVR_ATmega644__)
// Sanguino
// Two Wire (aka I2C) ports
uint8_t const SDA_PIN = 17;
uint8_t const SCL_PIN = 18;
// SPI port
uint8_t const SS_PIN = 4;
uint8_t const MOSI_PIN = 5;
uint8_t const MISO_PIN = 6;
uint8_t const SCK_PIN = 7;
static const pin_map_t digitalPinMap[] = {
{&DDRB, &PINB, &PORTB, 0}, // B0 0
{&DDRB, &PINB, &PORTB, 1}, // B1 1
{&DDRB, &PINB, &PORTB, 2}, // B2 2
{&DDRB, &PINB, &PORTB, 3}, // B3 3
{&DDRB, &PINB, &PORTB, 4}, // B4 4
{&DDRB, &PINB, &PORTB, 5}, // B5 5
{&DDRB, &PINB, &PORTB, 6}, // B6 6
{&DDRB, &PINB, &PORTB, 7}, // B7 7
{&DDRD, &PIND, &PORTD, 0}, // D0 8
{&DDRD, &PIND, &PORTD, 1}, // D1 9
{&DDRD, &PIND, &PORTD, 2}, // D2 10
{&DDRD, &PIND, &PORTD, 3}, // D3 11
{&DDRD, &PIND, &PORTD, 4}, // D4 12
{&DDRD, &PIND, &PORTD, 5}, // D5 13
{&DDRD, &PIND, &PORTD, 6}, // D6 14
{&DDRD, &PIND, &PORTD, 7}, // D7 15
{&DDRC, &PINC, &PORTC, 0}, // C0 16
{&DDRC, &PINC, &PORTC, 1}, // C1 17
{&DDRC, &PINC, &PORTC, 2}, // C2 18
{&DDRC, &PINC, &PORTC, 3}, // C3 19
{&DDRC, &PINC, &PORTC, 4}, // C4 20
{&DDRC, &PINC, &PORTC, 5}, // C5 21
{&DDRC, &PINC, &PORTC, 6}, // C6 22
{&DDRC, &PINC, &PORTC, 7}, // C7 23
{&DDRA, &PINA, &PORTA, 7}, // A7 24
{&DDRA, &PINA, &PORTA, 6}, // A6 25
{&DDRA, &PINA, &PORTA, 5}, // A5 26
{&DDRA, &PINA, &PORTA, 4}, // A4 27
{&DDRA, &PINA, &PORTA, 3}, // A3 28
{&DDRA, &PINA, &PORTA, 2}, // A2 29
{&DDRA, &PINA, &PORTA, 1}, // A1 30
{&DDRA, &PINA, &PORTA, 0} // A0 31
};
//------------------------------------------------------------------------------
#elif defined(__AVR_ATmega32U4__)
// Leonardo
// Two Wire (aka I2C) ports
uint8_t const SDA_PIN = 2;
uint8_t const SCL_PIN = 3;
// SPI port
uint8_t const SS_PIN = 17;
uint8_t const MOSI_PIN = 16;
uint8_t const MISO_PIN = 14;
uint8_t const SCK_PIN = 15;
static const pin_map_t digitalPinMap[] = {
{&DDRD, &PIND, &PORTD, 2}, // D2 0
{&DDRD, &PIND, &PORTD, 3}, // D3 1
{&DDRD, &PIND, &PORTD, 1}, // D1 2
{&DDRD, &PIND, &PORTD, 0}, // D0 3
{&DDRD, &PIND, &PORTD, 4}, // D4 4
{&DDRC, &PINC, &PORTC, 6}, // C6 5
{&DDRD, &PIND, &PORTD, 7}, // D7 6
{&DDRE, &PINE, &PORTE, 6}, // E6 7
{&DDRB, &PINB, &PORTB, 4}, // B4 8
{&DDRB, &PINB, &PORTB, 5}, // B5 9
{&DDRB, &PINB, &PORTB, 6}, // B6 10
{&DDRB, &PINB, &PORTB, 7}, // B7 11
{&DDRD, &PIND, &PORTD, 6}, // D6 12
{&DDRC, &PINC, &PORTC, 7}, // C7 13
{&DDRB, &PINB, &PORTB, 3}, // B3 14
{&DDRB, &PINB, &PORTB, 1}, // B1 15
{&DDRB, &PINB, &PORTB, 2}, // B2 16
{&DDRB, &PINB, &PORTB, 0}, // B0 17
{&DDRF, &PINF, &PORTF, 7}, // F7 18
{&DDRF, &PINF, &PORTF, 6}, // F6 19
{&DDRF, &PINF, &PORTF, 5}, // F5 20
{&DDRF, &PINF, &PORTF, 4}, // F4 21
{&DDRF, &PINF, &PORTF, 1}, // F1 22
{&DDRF, &PINF, &PORTF, 0}, // F0 23
};
//------------------------------------------------------------------------------
#elif defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB1286__)
// Teensy++ 1.0 & 2.0
// Two Wire (aka I2C) ports
uint8_t const SDA_PIN = 1;
uint8_t const SCL_PIN = 0;
// SPI port
uint8_t const SS_PIN = 20;
uint8_t const MOSI_PIN = 22;
uint8_t const MISO_PIN = 23;
uint8_t const SCK_PIN = 21;
static const pin_map_t digitalPinMap[] = {
{&DDRD, &PIND, &PORTD, 0}, // D0 0
{&DDRD, &PIND, &PORTD, 1}, // D1 1
{&DDRD, &PIND, &PORTD, 2}, // D2 2
{&DDRD, &PIND, &PORTD, 3}, // D3 3
{&DDRD, &PIND, &PORTD, 4}, // D4 4
{&DDRD, &PIND, &PORTD, 5}, // D5 5
{&DDRD, &PIND, &PORTD, 6}, // D6 6
{&DDRD, &PIND, &PORTD, 7}, // D7 7
{&DDRE, &PINE, &PORTE, 0}, // E0 8
{&DDRE, &PINE, &PORTE, 1}, // E1 9
{&DDRC, &PINC, &PORTC, 0}, // C0 10
{&DDRC, &PINC, &PORTC, 1}, // C1 11
{&DDRC, &PINC, &PORTC, 2}, // C2 12
{&DDRC, &PINC, &PORTC, 3}, // C3 13
{&DDRC, &PINC, &PORTC, 4}, // C4 14
{&DDRC, &PINC, &PORTC, 5}, // C5 15
{&DDRC, &PINC, &PORTC, 6}, // C6 16
{&DDRC, &PINC, &PORTC, 7}, // C7 17
{&DDRE, &PINE, &PORTE, 6}, // E6 18
{&DDRE, &PINE, &PORTE, 7}, // E7 19
{&DDRB, &PINB, &PORTB, 0}, // B0 20
{&DDRB, &PINB, &PORTB, 1}, // B1 21
{&DDRB, &PINB, &PORTB, 2}, // B2 22
{&DDRB, &PINB, &PORTB, 3}, // B3 23
{&DDRB, &PINB, &PORTB, 4}, // B4 24
{&DDRB, &PINB, &PORTB, 5}, // B5 25
{&DDRB, &PINB, &PORTB, 6}, // B6 26
{&DDRB, &PINB, &PORTB, 7}, // B7 27
{&DDRA, &PINA, &PORTA, 0}, // A0 28
{&DDRA, &PINA, &PORTA, 1}, // A1 29
{&DDRA, &PINA, &PORTA, 2}, // A2 30
{&DDRA, &PINA, &PORTA, 3}, // A3 31
{&DDRA, &PINA, &PORTA, 4}, // A4 32
{&DDRA, &PINA, &PORTA, 5}, // A5 33
{&DDRA, &PINA, &PORTA, 6}, // A6 34
{&DDRA, &PINA, &PORTA, 7}, // A7 35
{&DDRE, &PINE, &PORTE, 4}, // E4 36
{&DDRE, &PINE, &PORTE, 5}, // E5 37
{&DDRF, &PINF, &PORTF, 0}, // F0 38
{&DDRF, &PINF, &PORTF, 1}, // F1 39
{&DDRF, &PINF, &PORTF, 2}, // F2 40
{&DDRF, &PINF, &PORTF, 3}, // F3 41
{&DDRF, &PINF, &PORTF, 4}, // F4 42
{&DDRF, &PINF, &PORTF, 5}, // F5 43
{&DDRF, &PINF, &PORTF, 6}, // F6 44
{&DDRF, &PINF, &PORTF, 7} // F7 45
};
//------------------------------------------------------------------------------
#else // defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
// 168 and 328 Arduinos
// Two Wire (aka I2C) ports
uint8_t const SDA_PIN = 18;
uint8_t const SCL_PIN = 19;
// SPI port
uint8_t const SS_PIN = 10;
uint8_t const MOSI_PIN = 11;
uint8_t const MISO_PIN = 12;
uint8_t const SCK_PIN = 13;
static const pin_map_t digitalPinMap[] = {
{&DDRD, &PIND, &PORTD, 0}, // D0 0
{&DDRD, &PIND, &PORTD, 1}, // D1 1
{&DDRD, &PIND, &PORTD, 2}, // D2 2
{&DDRD, &PIND, &PORTD, 3}, // D3 3
{&DDRD, &PIND, &PORTD, 4}, // D4 4
{&DDRD, &PIND, &PORTD, 5}, // D5 5
{&DDRD, &PIND, &PORTD, 6}, // D6 6
{&DDRD, &PIND, &PORTD, 7}, // D7 7
{&DDRB, &PINB, &PORTB, 0}, // B0 8
{&DDRB, &PINB, &PORTB, 1}, // B1 9
{&DDRB, &PINB, &PORTB, 2}, // B2 10
{&DDRB, &PINB, &PORTB, 3}, // B3 11
{&DDRB, &PINB, &PORTB, 4}, // B4 12
{&DDRB, &PINB, &PORTB, 5}, // B5 13
{&DDRC, &PINC, &PORTC, 0}, // C0 14
{&DDRC, &PINC, &PORTC, 1}, // C1 15
{&DDRC, &PINC, &PORTC, 2}, // C2 16
{&DDRC, &PINC, &PORTC, 3}, // C3 17
{&DDRC, &PINC, &PORTC, 4}, // C4 18
{&DDRC, &PINC, &PORTC, 5} // C5 19
};
#endif // defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
//------------------------------------------------------------------------------
static const uint8_t digitalPinCount = sizeof(digitalPinMap) / sizeof(pin_map_t);
uint8_t badPinNumber(void)
__attribute__((error("Pin number is too large or not a constant")));
static inline __attribute__((always_inline))
uint8_t getPinMode(uint8_t pin) {
if (__builtin_constant_p(pin) && pin < digitalPinCount) {
return (*digitalPinMap[pin].ddr >> digitalPinMap[pin].bit) & 1;
} else {
return badPinNumber();
}
}
static inline __attribute__((always_inline))
void setPinMode(uint8_t pin, uint8_t mode) {
if (__builtin_constant_p(pin) && pin < digitalPinCount) {
if (mode) {
*digitalPinMap[pin].ddr |= 1 << digitalPinMap[pin].bit;
} else {
*digitalPinMap[pin].ddr &= ~(1 << digitalPinMap[pin].bit);
}
} else {
badPinNumber();
}
}
static inline __attribute__((always_inline))
uint8_t fastDigitalRead(uint8_t pin) {
if (__builtin_constant_p(pin) && pin < digitalPinCount) {
return (*digitalPinMap[pin].pin >> digitalPinMap[pin].bit) & 1;
} else {
return badPinNumber();
}
}
static inline __attribute__((always_inline))
void fastDigitalWrite(uint8_t pin, uint8_t value) {
if (__builtin_constant_p(pin) && pin < digitalPinCount) {
if (value) {
*digitalPinMap[pin].port |= 1 << digitalPinMap[pin].bit;
} else {
*digitalPinMap[pin].port &= ~(1 << digitalPinMap[pin].bit);
}
} else {
badPinNumber();
}
}
#endif // Sd2PinMap_h
#elif defined (__CPU_ARC__)
#if defined (__ARDUINO_ARC__)
// Two Wire (aka I2C) ports
uint8_t const SDA_PIN = 18;
uint8_t const SCL_PIN = 19;
// SPI port
uint8_t const SS_PIN = 10;
uint8_t const MOSI_PIN = 11;
uint8_t const MISO_PIN = 12;
uint8_t const SCK_PIN = 13;
#endif // Arduino ARC
#else
#error Architecture or board not supported.
#endif

641
libraries/SD/src/utility/SdFat.h Executable file
View File

@ -0,0 +1,641 @@
/* Arduino SdFat Library
Copyright (C) 2009 by William Greiman
This file is part of the Arduino SdFat Library
This Library is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with the Arduino SdFat Library. If not, see
<http://www.gnu.org/licenses/>.
*/
#ifndef SdFat_h
#define SdFat_h
/**
\file
SdFile and SdVolume classes
*/
#if defined (__AVR__) || defined (__CPU_ARC__)
#include <avr/pgmspace.h>
#endif
#include "Sd2Card.h"
#include "FatStructs.h"
#include <Print.h>
//------------------------------------------------------------------------------
/**
Allow use of deprecated functions if non-zero
*/
#define ALLOW_DEPRECATED_FUNCTIONS 1
//------------------------------------------------------------------------------
// forward declaration since SdVolume is used in SdFile
class SdVolume;
//==============================================================================
// SdFile class
#ifdef O_RDONLY //ARDUINO_ARCH_MBED
#undef O_READ
#undef O_RDONLY
#undef O_WRITE
#undef O_WRONLY
#undef O_RDWR
#undef O_ACCMODE
#undef O_APPEND
#undef O_SYNC
#undef O_CREAT
#undef O_EXCL
#undef O_TRUNC
#endif
// flags for ls()
/** ls() flag to print modify date */
uint8_t const LS_DATE = 1;
/** ls() flag to print file size */
uint8_t const LS_SIZE = 2;
/** ls() flag for recursive list of subdirectories */
uint8_t const LS_R = 4;
// use the gnu style oflag in open()
/** open() oflag for reading */
uint8_t const O_READ = 0X01;
/** open() oflag - same as O_READ */
uint8_t const O_RDONLY = O_READ;
/** open() oflag for write */
uint8_t const O_WRITE = 0X02;
/** open() oflag - same as O_WRITE */
uint8_t const O_WRONLY = O_WRITE;
/** open() oflag for reading and writing */
uint8_t const O_RDWR = (O_READ | O_WRITE);
/** open() oflag mask for access modes */
uint8_t const O_ACCMODE = (O_READ | O_WRITE);
/** The file offset shall be set to the end of the file prior to each write. */
uint8_t const O_APPEND = 0X04;
/** synchronous writes - call sync() after each write */
uint8_t const O_SYNC = 0X08;
/** create the file if nonexistent */
uint8_t const O_CREAT = 0X10;
/** If O_CREAT and O_EXCL are set, open() shall fail if the file exists */
uint8_t const O_EXCL = 0X20;
/** truncate the file to zero length */
uint8_t const O_TRUNC = 0X40;
// flags for timestamp
/** set the file's last access date */
uint8_t const T_ACCESS = 1;
/** set the file's creation date and time */
uint8_t const T_CREATE = 2;
/** Set the file's write date and time */
uint8_t const T_WRITE = 4;
// values for type_
/** This SdFile has not been opened. */
uint8_t const FAT_FILE_TYPE_CLOSED = 0;
/** SdFile for a file */
uint8_t const FAT_FILE_TYPE_NORMAL = 1;
/** SdFile for a FAT16 root directory */
uint8_t const FAT_FILE_TYPE_ROOT16 = 2;
/** SdFile for a FAT32 root directory */
uint8_t const FAT_FILE_TYPE_ROOT32 = 3;
/** SdFile for a subdirectory */
uint8_t const FAT_FILE_TYPE_SUBDIR = 4;
/** Test value for directory type */
uint8_t const FAT_FILE_TYPE_MIN_DIR = FAT_FILE_TYPE_ROOT16;
/** date field for FAT directory entry */
static inline uint16_t FAT_DATE(uint16_t year, uint8_t month, uint8_t day) {
return (year - 1980) << 9 | month << 5 | day;
}
/** year part of FAT directory date field */
static inline uint16_t FAT_YEAR(uint16_t fatDate) {
return 1980 + (fatDate >> 9);
}
/** month part of FAT directory date field */
static inline uint8_t FAT_MONTH(uint16_t fatDate) {
return (fatDate >> 5) & 0XF;
}
/** day part of FAT directory date field */
static inline uint8_t FAT_DAY(uint16_t fatDate) {
return fatDate & 0X1F;
}
/** time field for FAT directory entry */
static inline uint16_t FAT_TIME(uint8_t hour, uint8_t minute, uint8_t second) {
return hour << 11 | minute << 5 | second >> 1;
}
/** hour part of FAT directory time field */
static inline uint8_t FAT_HOUR(uint16_t fatTime) {
return fatTime >> 11;
}
/** minute part of FAT directory time field */
static inline uint8_t FAT_MINUTE(uint16_t fatTime) {
return (fatTime >> 5) & 0X3F;
}
/** second part of FAT directory time field */
static inline uint8_t FAT_SECOND(uint16_t fatTime) {
return 2 * (fatTime & 0X1F);
}
/** Default date for file timestamps is 1 Jan 2000 */
uint16_t const FAT_DEFAULT_DATE = ((2000 - 1980) << 9) | (1 << 5) | 1;
/** Default time for file timestamp is 1 am */
uint16_t const FAT_DEFAULT_TIME = (1 << 11);
//------------------------------------------------------------------------------
/**
\class SdFile
\brief Access FAT16 and FAT32 files on SD and SDHC cards.
*/
class SdFile : public Print {
public:
/** Create an instance of SdFile. */
SdFile(void) : type_(FAT_FILE_TYPE_CLOSED) {}
/**
writeError is set to true if an error occurs during a write().
Set writeError to false before calling print() and/or write() and check
for true after calls to print() and/or write().
*/
//bool writeError;
/**
Cancel unbuffered reads for this file.
See setUnbufferedRead()
*/
void clearUnbufferedRead(void) {
flags_ &= ~F_FILE_UNBUFFERED_READ;
}
uint8_t close(void);
uint8_t contiguousRange(uint32_t* bgnBlock, uint32_t* endBlock);
uint8_t createContiguous(SdFile* dirFile,
const char* fileName, uint32_t size);
/** \return The current cluster number for a file or directory. */
uint32_t curCluster(void) const {
return curCluster_;
}
/** \return The current position for a file or directory. */
uint32_t curPosition(void) const {
return curPosition_;
}
/**
Set the date/time callback function
\param[in] dateTime The user's call back function. The callback
function is of the form:
\code
void dateTime(uint16_t* date, uint16_t* time) {
uint16_t year;
uint8_t month, day, hour, minute, second;
// User gets date and time from GPS or real-time clock here
// return date using FAT_DATE macro to format fields
* *date = FAT_DATE(year, month, day);
// return time using FAT_TIME macro to format fields
* *time = FAT_TIME(hour, minute, second);
}
\endcode
Sets the function that is called when a file is created or when
a file's directory entry is modified by sync(). All timestamps,
access, creation, and modify, are set when a file is created.
sync() maintains the last access date and last modify date/time.
See the timestamp() function.
*/
static void dateTimeCallback(
void (*dateTime)(uint16_t* date, uint16_t* time)) {
dateTime_ = dateTime;
}
/**
Cancel the date/time callback function.
*/
static void dateTimeCallbackCancel(void) {
// use explicit zero since NULL is not defined for Sanguino
dateTime_ = 0;
}
/** \return Address of the block that contains this file's directory. */
uint32_t dirBlock(void) const {
return dirBlock_;
}
uint8_t dirEntry(dir_t* dir);
/** \return Index of this file's directory in the block dirBlock. */
uint8_t dirIndex(void) const {
return dirIndex_;
}
static void dirName(const dir_t& dir, char* name);
/** \return The total number of bytes in a file or directory. */
uint32_t fileSize(void) const {
return fileSize_;
}
/** \return The first cluster number for a file or directory. */
uint32_t firstCluster(void) const {
return firstCluster_;
}
/** \return True if this is a SdFile for a directory else false. */
uint8_t isDir(void) const {
return type_ >= FAT_FILE_TYPE_MIN_DIR;
}
/** \return True if this is a SdFile for a file else false. */
uint8_t isFile(void) const {
return type_ == FAT_FILE_TYPE_NORMAL;
}
/** \return True if this is a SdFile for an open file/directory else false. */
uint8_t isOpen(void) const {
return type_ != FAT_FILE_TYPE_CLOSED;
}
/** \return True if this is a SdFile for a subdirectory else false. */
uint8_t isSubDir(void) const {
return type_ == FAT_FILE_TYPE_SUBDIR;
}
/** \return True if this is a SdFile for the root directory. */
uint8_t isRoot(void) const {
return type_ == FAT_FILE_TYPE_ROOT16 || type_ == FAT_FILE_TYPE_ROOT32;
}
void ls(uint8_t flags = 0, uint8_t indent = 0);
uint8_t makeDir(SdFile* dir, const char* dirName);
uint8_t open(SdFile* dirFile, uint16_t index, uint8_t oflag);
uint8_t open(SdFile* dirFile, const char* fileName, uint8_t oflag);
uint8_t openRoot(SdVolume* vol);
static void printDirName(const dir_t& dir, uint8_t width);
static void printFatDate(uint16_t fatDate);
static void printFatTime(uint16_t fatTime);
static void printTwoDigits(uint8_t v);
/**
Read the next byte from a file.
\return For success read returns the next byte in the file as an int.
If an error occurs or end of file is reached -1 is returned.
*/
int16_t read(void) {
uint8_t b;
return read(&b, 1) == 1 ? b : -1;
}
int16_t read(void* buf, uint16_t nbyte);
int8_t readDir(dir_t* dir);
static uint8_t remove(SdFile* dirFile, const char* fileName);
uint8_t remove(void);
/** Set the file's current position to zero. */
void rewind(void) {
curPosition_ = curCluster_ = 0;
}
uint8_t rmDir(void);
uint8_t rmRfStar(void);
/** Set the files position to current position + \a pos. See seekSet(). */
uint8_t seekCur(uint32_t pos) {
return seekSet(curPosition_ + pos);
}
/**
Set the files current position to end of file. Useful to position
a file for append. See seekSet().
*/
uint8_t seekEnd(void) {
return seekSet(fileSize_);
}
uint8_t seekSet(uint32_t pos);
/**
Use unbuffered reads to access this file. Used with Wave
Shield ISR. Used with Sd2Card::partialBlockRead() in WaveRP.
Not recommended for normal applications.
*/
void setUnbufferedRead(void) {
if (isFile()) {
flags_ |= F_FILE_UNBUFFERED_READ;
}
}
uint8_t timestamp(uint8_t flag, uint16_t year, uint8_t month, uint8_t day,
uint8_t hour, uint8_t minute, uint8_t second);
uint8_t sync(uint8_t blocking = 1);
/** Type of this SdFile. You should use isFile() or isDir() instead of type()
if possible.
\return The file or directory type.
*/
uint8_t type(void) const {
return type_;
}
uint8_t truncate(uint32_t size);
/** \return Unbuffered read flag. */
uint8_t unbufferedRead(void) const {
return flags_ & F_FILE_UNBUFFERED_READ;
}
/** \return SdVolume that contains this file. */
SdVolume* volume(void) const {
return vol_;
}
size_t write(uint8_t b);
size_t write(const void* buf, uint16_t nbyte);
size_t write(const char* str);
#ifdef __AVR__
void write_P(PGM_P str);
void writeln_P(PGM_P str);
#endif
int availableForWrite(void);
//------------------------------------------------------------------------------
#if ALLOW_DEPRECATED_FUNCTIONS
// Deprecated functions - suppress cpplint warnings with NOLINT comment
/** \deprecated Use:
uint8_t SdFile::contiguousRange(uint32_t* bgnBlock, uint32_t* endBlock);
*/
uint8_t contiguousRange(uint32_t& bgnBlock, uint32_t& endBlock) { // NOLINT
return contiguousRange(&bgnBlock, &endBlock);
}
/** \deprecated Use:
uint8_t SdFile::createContiguous(SdFile* dirFile,
const char* fileName, uint32_t size)
*/
uint8_t createContiguous(SdFile& dirFile, // NOLINT
const char* fileName, uint32_t size) {
return createContiguous(&dirFile, fileName, size);
}
/**
\deprecated Use:
static void SdFile::dateTimeCallback(
void (*dateTime)(uint16_t* date, uint16_t* time));
*/
static void dateTimeCallback(
void (*dateTime)(uint16_t& date, uint16_t& time)) { // NOLINT
oldDateTime_ = dateTime;
dateTime_ = dateTime ? oldToNew : 0;
}
/** \deprecated Use: uint8_t SdFile::dirEntry(dir_t* dir); */
uint8_t dirEntry(dir_t& dir) {
return dirEntry(&dir); // NOLINT
}
/** \deprecated Use:
uint8_t SdFile::makeDir(SdFile* dir, const char* dirName);
*/
uint8_t makeDir(SdFile& dir, const char* dirName) { // NOLINT
return makeDir(&dir, dirName);
}
/** \deprecated Use:
uint8_t SdFile::open(SdFile* dirFile, const char* fileName, uint8_t oflag);
*/
uint8_t open(SdFile& dirFile, // NOLINT
const char* fileName, uint8_t oflag) {
return open(&dirFile, fileName, oflag);
}
/** \deprecated Do not use in new apps */
uint8_t open(SdFile& dirFile, const char* fileName) { // NOLINT
return open(dirFile, fileName, O_RDWR);
}
/** \deprecated Use:
uint8_t SdFile::open(SdFile* dirFile, uint16_t index, uint8_t oflag);
*/
uint8_t open(SdFile& dirFile, uint16_t index, uint8_t oflag) { // NOLINT
return open(&dirFile, index, oflag);
}
/** \deprecated Use: uint8_t SdFile::openRoot(SdVolume* vol); */
uint8_t openRoot(SdVolume& vol) {
return openRoot(&vol); // NOLINT
}
/** \deprecated Use: int8_t SdFile::readDir(dir_t* dir); */
int8_t readDir(dir_t& dir) {
return readDir(&dir); // NOLINT
}
/** \deprecated Use:
static uint8_t SdFile::remove(SdFile* dirFile, const char* fileName);
*/
static uint8_t remove(SdFile& dirFile, const char* fileName) { // NOLINT
return remove(&dirFile, fileName);
}
//------------------------------------------------------------------------------
// rest are private
private:
static void (*oldDateTime_)(uint16_t& date, uint16_t& time); // NOLINT
static void oldToNew(uint16_t* date, uint16_t* time) {
uint16_t d;
uint16_t t;
oldDateTime_(d, t);
*date = d;
*time = t;
}
#endif // ALLOW_DEPRECATED_FUNCTIONS
private:
// bits defined in flags_
// should be 0XF
static uint8_t const F_OFLAG = (O_ACCMODE | O_APPEND | O_SYNC);
// available bits
static uint8_t const F_FILE_NON_BLOCKING_WRITE = 0X10;
// a new cluster was added to the file
static uint8_t const F_FILE_CLUSTER_ADDED = 0X20;
// use unbuffered SD read
static uint8_t const F_FILE_UNBUFFERED_READ = 0X40;
// sync of directory entry required
static uint8_t const F_FILE_DIR_DIRTY = 0X80;
// make sure F_OFLAG is ok
#if ((F_FILE_NON_BLOCKING_WRITE | F_FILE_CLUSTER_ADDED | F_FILE_UNBUFFERED_READ | F_FILE_DIR_DIRTY) & F_OFLAG)
#error flags_ bits conflict
#endif // flags_ bits
// private data
uint8_t flags_; // See above for definition of flags_ bits
uint8_t type_; // type of file see above for values
uint32_t curCluster_; // cluster for current file position
uint32_t curPosition_; // current file position in bytes from beginning
uint32_t dirBlock_; // SD block that contains directory entry for file
uint8_t dirIndex_; // index of entry in dirBlock 0 <= dirIndex_ <= 0XF
uint32_t fileSize_; // file size in bytes
uint32_t firstCluster_; // first cluster of file
SdVolume* vol_; // volume where file is located
// private functions
uint8_t addCluster(void);
uint8_t addDirCluster(void);
dir_t* cacheDirEntry(uint8_t action);
static void (*dateTime_)(uint16_t* date, uint16_t* time);
static uint8_t make83Name(const char* str, uint8_t* name);
uint8_t openCachedEntry(uint8_t cacheIndex, uint8_t oflags);
dir_t* readDirCache(void);
};
//==============================================================================
// SdVolume class
/**
\brief Cache for an SD data block
*/
union cache_t {
/** Used to access cached file data blocks. */
uint8_t data[512];
/** Used to access cached FAT16 entries. */
uint16_t fat16[256];
/** Used to access cached FAT32 entries. */
uint32_t fat32[128];
/** Used to access cached directory entries. */
dir_t dir[16];
/** Used to access a cached MasterBoot Record. */
mbr_t mbr;
/** Used to access to a cached FAT boot sector. */
fbs_t fbs;
};
//------------------------------------------------------------------------------
/**
\class SdVolume
\brief Access FAT16 and FAT32 volumes on SD and SDHC cards.
*/
class SdVolume {
public:
/** Create an instance of SdVolume */
SdVolume(void) : allocSearchStart_(2), fatType_(0) {}
/** Clear the cache and returns a pointer to the cache. Used by the WaveRP
recorder to do raw write to the SD card. Not for normal apps.
*/
static uint8_t* cacheClear(void) {
cacheFlush();
cacheBlockNumber_ = 0XFFFFFFFF;
return cacheBuffer_.data;
}
/**
Initialize a FAT volume. Try partition one first then try super
floppy format.
\param[in] dev The Sd2Card where the volume is located.
\return The value one, true, is returned for success and
the value zero, false, is returned for failure. Reasons for
failure include not finding a valid partition, not finding a valid
FAT file system or an I/O error.
*/
uint8_t init(Sd2Card* dev) {
return init(dev, 1) ? true : init(dev, 0);
}
uint8_t init(Sd2Card* dev, uint8_t part);
// inline functions that return volume info
/** \return The volume's cluster size in blocks. */
uint8_t blocksPerCluster(void) const {
return blocksPerCluster_;
}
/** \return The number of blocks in one FAT. */
uint32_t blocksPerFat(void) const {
return blocksPerFat_;
}
/** \return The total number of clusters in the volume. */
uint32_t clusterCount(void) const {
return clusterCount_;
}
/** \return The shift count required to multiply by blocksPerCluster. */
uint8_t clusterSizeShift(void) const {
return clusterSizeShift_;
}
/** \return The logical block number for the start of file data. */
uint32_t dataStartBlock(void) const {
return dataStartBlock_;
}
/** \return The number of FAT structures on the volume. */
uint8_t fatCount(void) const {
return fatCount_;
}
/** \return The logical block number for the start of the first FAT. */
uint32_t fatStartBlock(void) const {
return fatStartBlock_;
}
/** \return The FAT type of the volume. Values are 12, 16 or 32. */
uint8_t fatType(void) const {
return fatType_;
}
/** \return The number of entries in the root directory for FAT16 volumes. */
uint32_t rootDirEntryCount(void) const {
return rootDirEntryCount_;
}
/** \return The logical block number for the start of the root directory
on FAT16 volumes or the first cluster number on FAT32 volumes. */
uint32_t rootDirStart(void) const {
return rootDirStart_;
}
/** return a pointer to the Sd2Card object for this volume */
static Sd2Card* sdCard(void) {
return sdCard_;
}
//------------------------------------------------------------------------------
#if ALLOW_DEPRECATED_FUNCTIONS
// Deprecated functions - suppress cpplint warnings with NOLINT comment
/** \deprecated Use: uint8_t SdVolume::init(Sd2Card* dev); */
uint8_t init(Sd2Card& dev) {
return init(&dev); // NOLINT
}
/** \deprecated Use: uint8_t SdVolume::init(Sd2Card* dev, uint8_t vol); */
uint8_t init(Sd2Card& dev, uint8_t part) { // NOLINT
return init(&dev, part);
}
#endif // ALLOW_DEPRECATED_FUNCTIONS
//------------------------------------------------------------------------------
private:
// Allow SdFile access to SdVolume private data.
friend class SdFile;
// value for action argument in cacheRawBlock to indicate read from cache
static uint8_t const CACHE_FOR_READ = 0;
// value for action argument in cacheRawBlock to indicate cache dirty
static uint8_t const CACHE_FOR_WRITE = 1;
static cache_t cacheBuffer_; // 512 byte cache for device blocks
static uint32_t cacheBlockNumber_; // Logical number of block in the cache
static Sd2Card* sdCard_; // Sd2Card object for cache
static uint8_t cacheDirty_; // cacheFlush() will write block if true
static uint32_t cacheMirrorBlock_; // block number for mirror FAT
//
uint32_t allocSearchStart_; // start cluster for alloc search
uint8_t blocksPerCluster_; // cluster size in blocks
uint32_t blocksPerFat_; // FAT size in blocks
uint32_t clusterCount_; // clusters in one FAT
uint8_t clusterSizeShift_; // shift to convert cluster count to block count
uint32_t dataStartBlock_; // first data block number
uint8_t fatCount_; // number of FATs on volume
uint32_t fatStartBlock_; // start block for first FAT
uint8_t fatType_; // volume type (12, 16, OR 32)
uint16_t rootDirEntryCount_; // number of entries in FAT16 root dir
uint32_t rootDirStart_; // root start block for FAT16, cluster for FAT32
//----------------------------------------------------------------------------
uint8_t allocContiguous(uint32_t count, uint32_t* curCluster);
uint8_t blockOfCluster(uint32_t position) const {
return (position >> 9) & (blocksPerCluster_ - 1);
}
uint32_t clusterStartBlock(uint32_t cluster) const {
return dataStartBlock_ + ((cluster - 2) << clusterSizeShift_);
}
uint32_t blockNumber(uint32_t cluster, uint32_t position) const {
return clusterStartBlock(cluster) + blockOfCluster(position);
}
static uint8_t cacheFlush(uint8_t blocking = 1);
static uint8_t cacheMirrorBlockFlush(uint8_t blocking);
static uint8_t cacheRawBlock(uint32_t blockNumber, uint8_t action);
static void cacheSetDirty(void) {
cacheDirty_ |= CACHE_FOR_WRITE;
}
static uint8_t cacheZeroBlock(uint32_t blockNumber);
uint8_t chainSize(uint32_t beginCluster, uint32_t* size) const;
uint8_t fatGet(uint32_t cluster, uint32_t* value) const;
uint8_t fatPut(uint32_t cluster, uint32_t value);
uint8_t fatPutEOC(uint32_t cluster) {
return fatPut(cluster, 0x0FFFFFFF);
}
uint8_t freeChain(uint32_t cluster);
uint8_t isEOC(uint32_t cluster) const {
return cluster >= (fatType_ == 16 ? FAT16EOC_MIN : FAT32EOC_MIN);
}
uint8_t readBlock(uint32_t block, uint8_t* dst) {
return sdCard_->readBlock(block, dst);
}
uint8_t readData(uint32_t block, uint16_t offset,
uint16_t count, uint8_t* dst) {
return sdCard_->readData(block, offset, count, dst);
}
uint8_t writeBlock(uint32_t block, const uint8_t* dst, uint8_t blocking = 1) {
return sdCard_->writeBlock(block, dst, blocking);
}
uint8_t isBusy(void) {
return sdCard_->isBusy();
}
uint8_t isCacheMirrorBlockDirty(void) {
return (cacheMirrorBlock_ != 0);
}
};
#endif // SdFat_h

View File

@ -0,0 +1,77 @@
/* Arduino SdFat Library
Copyright (C) 2008 by William Greiman
This file is part of the Arduino SdFat Library
This Library is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with the Arduino SdFat Library. If not, see
<http://www.gnu.org/licenses/>.
*/
#ifndef SdFatUtil_h
#define SdFatUtil_h
/**
\file
Useful utility functions.
*/
#include <Arduino.h>
#ifdef __AVR__
#include <avr/pgmspace.h>
/** Store and print a string in flash memory.*/
#define PgmPrint(x) SerialPrint_P(PSTR(x))
/** Store and print a string in flash memory followed by a CR/LF.*/
#define PgmPrintln(x) SerialPrintln_P(PSTR(x))
/** Defined so doxygen works for function definitions. */
#endif
#define NOINLINE __attribute__((noinline,unused))
#define UNUSEDOK __attribute__((unused))
//------------------------------------------------------------------------------
/** Return the number of bytes currently free in RAM. */
static UNUSEDOK int FreeRam(void) {
extern int __bss_end;
extern int* __brkval;
int free_memory;
if (reinterpret_cast<int>(__brkval) == 0) {
// if no heap use from end of bss section
free_memory = reinterpret_cast<int>(&free_memory)
- reinterpret_cast<int>(&__bss_end);
} else {
// use from top of stack to heap
free_memory = reinterpret_cast<int>(&free_memory)
- reinterpret_cast<int>(__brkval);
}
return free_memory;
}
#ifdef __AVR__
//------------------------------------------------------------------------------
/**
%Print a string in flash memory to the serial port.
\param[in] str Pointer to string stored in flash memory.
*/
static NOINLINE void SerialPrint_P(PGM_P str) {
for (uint8_t c; (c = pgm_read_byte(str)); str++) {
Serial.write(c);
}
}
//------------------------------------------------------------------------------
/**
%Print a string in flash memory followed by a CR/LF.
\param[in] str Pointer to string stored in flash memory.
*/
static NOINLINE void SerialPrintln_P(PGM_P str) {
SerialPrint_P(str);
Serial.println();
}
#endif // __AVR__
#endif // #define SdFatUtil_h

View File

@ -0,0 +1,202 @@
/* Arduino SdFat Library
Copyright (C) 2009 by William Greiman
This file is part of the Arduino SdFat Library
This Library is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with the Arduino SdFat Library. If not, see
<http://www.gnu.org/licenses/>.
*/
/**
\mainpage Arduino SdFat Library
<CENTER>Copyright &copy; 2009 by William Greiman
</CENTER>
\section Intro Introduction
The Arduino SdFat Library is a minimal implementation of FAT16 and FAT32
file systems on SD flash memory cards. Standard SD and high capacity
SDHC cards are supported.
The SdFat only supports short 8.3 names.
The main classes in SdFat are Sd2Card, SdVolume, and SdFile.
The Sd2Card class supports access to standard SD cards and SDHC cards. Most
applications will only need to call the Sd2Card::init() member function.
The SdVolume class supports FAT16 and FAT32 partitions. Most applications
will only need to call the SdVolume::init() member function.
The SdFile class provides file access functions such as open(), read(),
remove(), write(), close() and sync(). This class supports access to the root
directory and subdirectories.
A number of example are provided in the SdFat/examples folder. These were
developed to test SdFat and illustrate its use.
SdFat was developed for high speed data recording. SdFat was used to implement
an audio record/play class, WaveRP, for the Adafruit Wave Shield. This
application uses special Sd2Card calls to write to contiguous files in raw mode.
These functions reduce write latency so that audio can be recorded with the
small amount of RAM in the Arduino.
\section SDcard SD\SDHC Cards
Arduinos access SD cards using the cards SPI protocol. PCs, Macs, and
most consumer devices use the 4-bit parallel SD protocol. A card that
functions well on A PC or Mac may not work well on the Arduino.
Most cards have good SPI read performance but cards vary widely in SPI
write performance. Write performance is limited by how efficiently the
card manages internal erase/remapping operations. The Arduino cannot
optimize writes to reduce erase operations because of its limit RAM.
SanDisk cards generally have good write performance. They seem to have
more internal RAM buffering than other cards and therefore can limit
the number of flash erase operations that the Arduino forces due to its
limited RAM.
\section Hardware Hardware Configuration
SdFat was developed using an
<A HREF = "http://www.adafruit.com/"> Adafruit Industries</A>
<A HREF = "http://www.ladyada.net/make/waveshield/"> Wave Shield</A>.
The hardware interface to the SD card should not use a resistor based level
shifter. SdFat sets the SPI bus frequency to 8 MHz which results in signal
rise times that are too slow for the edge detectors in many newer SD card
controllers when resistor voltage dividers are used.
The 5 to 3.3 V level shifter for 5 V Arduinos should be IC based like the
74HC4050N based circuit shown in the file SdLevel.png. The Adafruit Wave Shield
uses a 74AHC125N. Gravitech sells SD and MicroSD Card Adapters based on the
74LCX245.
If you are using a resistor based level shifter and are having problems try
setting the SPI bus frequency to 4 MHz. This can be done by using
card.init(SPI_HALF_SPEED) to initialize the SD card.
\section comment Bugs and Comments
If you wish to report bugs or have comments, send email to fat16lib@sbcglobal.net.
\section SdFatClass SdFat Usage
SdFat uses a slightly restricted form of short names.
Only printable ASCII characters are supported. No characters with code point
values greater than 127 are allowed. Space is not allowed even though space
was allowed in the API of early versions of DOS.
Short names are limited to 8 characters followed by an optional period (.)
and extension of up to 3 characters. The characters may be any combination
of letters and digits. The following special characters are also allowed:
$ % ' - _ @ ~ ` ! ( ) { } ^ # &
Short names are always converted to upper case and their original case
value is lost.
\note
The Arduino Print class uses character
at a time writes so it was necessary to use a \link SdFile::sync() sync() \endlink
function to control when data is written to the SD card.
\par
An application which writes to a file using \link Print::print() print()\endlink,
\link Print::println() println() \endlink
or \link SdFile::write write() \endlink must call \link SdFile::sync() sync() \endlink
at the appropriate time to force data and directory information to be written
to the SD Card. Data and directory information are also written to the SD card
when \link SdFile::close() close() \endlink is called.
\par
Applications must use care calling \link SdFile::sync() sync() \endlink
since 2048 bytes of I/O is required to update file and
directory information. This includes writing the current data block, reading
the block that contains the directory entry for update, writing the directory
block back and reading back the current data block.
It is possible to open a file with two or more instances of SdFile. A file may
be corrupted if data is written to the file by more than one instance of SdFile.
\section HowTo How to format SD Cards as FAT Volumes
You should use a freshly formatted SD card for best performance. FAT
file systems become slower if many files have been created and deleted.
This is because the directory entry for a deleted file is marked as deleted,
but is not deleted. When a new file is created, these entries must be scanned
before creating the file, a flaw in the FAT design. Also files can become
fragmented which causes reads and writes to be slower.
Microsoft operating systems support removable media formatted with a
Master Boot Record, MBR, or formatted as a super floppy with a FAT Boot Sector
in block zero.
Microsoft operating systems expect MBR formatted removable media
to have only one partition. The first partition should be used.
Microsoft operating systems do not support partitioning SD flash cards.
If you erase an SD card with a program like KillDisk, Most versions of
Windows will format the card as a super floppy.
The best way to restore an SD card's format is to use SDFormatter
which can be downloaded from:
http://www.sdcard.org/consumers/formatter/
SDFormatter aligns flash erase boundaries with file
system structures which reduces write latency and file system overhead.
SDFormatter does not have an option for FAT type so it may format
small cards as FAT12.
After the MBR is restored by SDFormatter you may need to reformat small
cards that have been formatted FAT12 to force the volume type to be FAT16.
If you reformat the SD card with an OS utility, choose a cluster size that
will result in:
4084 < CountOfClusters && CountOfClusters < 65525
The volume will then be FAT16.
If you are formatting an SD card on OS X or Linux, be sure to use the first
partition. Format this partition with a cluster count in above range.
\section References References
Adafruit Industries:
http://www.adafruit.com/
http://www.ladyada.net/make/waveshield/
The Arduino site:
http://www.arduino.cc/
For more information about FAT file systems see:
http://www.microsoft.com/whdc/system/platform/firmware/fatgen.mspx
For information about using SD cards as SPI devices see:
http://www.sdcard.org/developers/tech/sdcard/pls/Simplified_Physical_Layer_Spec.pdf
The ATmega328 datasheet:
http://www.atmel.com/dyn/resources/prod_documents/doc8161.pdf
*/

File diff suppressed because it is too large Load Diff

232
libraries/SD/src/utility/SdInfo.h Executable file
View File

@ -0,0 +1,232 @@
/* Arduino Sd2Card Library
Copyright (C) 2009 by William Greiman
This file is part of the Arduino Sd2Card Library
This Library is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with the Arduino Sd2Card Library. If not, see
<http://www.gnu.org/licenses/>.
*/
#ifndef SdInfo_h
#define SdInfo_h
#include <stdint.h>
// Based on the document:
//
// SD Specifications
// Part 1
// Physical Layer
// Simplified Specification
// Version 2.00
// September 25, 2006
//
// www.sdcard.org/developers/tech/sdcard/pls/Simplified_Physical_Layer_Spec.pdf
//------------------------------------------------------------------------------
// SD card commands
/** GO_IDLE_STATE - init card in spi mode if CS low */
uint8_t const CMD0 = 0X00;
/** SEND_IF_COND - verify SD Memory Card interface operating condition.*/
uint8_t const CMD8 = 0X08;
/** SEND_CSD - read the Card Specific Data (CSD register) */
uint8_t const CMD9 = 0X09;
/** SEND_CID - read the card identification information (CID register) */
uint8_t const CMD10 = 0X0A;
/** SEND_STATUS - read the card status register */
uint8_t const CMD13 = 0X0D;
/** READ_BLOCK - read a single data block from the card */
uint8_t const CMD17 = 0X11;
/** WRITE_BLOCK - write a single data block to the card */
uint8_t const CMD24 = 0X18;
/** WRITE_MULTIPLE_BLOCK - write blocks of data until a STOP_TRANSMISSION */
uint8_t const CMD25 = 0X19;
/** ERASE_WR_BLK_START - sets the address of the first block to be erased */
uint8_t const CMD32 = 0X20;
/** ERASE_WR_BLK_END - sets the address of the last block of the continuous
range to be erased*/
uint8_t const CMD33 = 0X21;
/** ERASE - erase all previously selected blocks */
uint8_t const CMD38 = 0X26;
/** APP_CMD - escape for application specific command */
uint8_t const CMD55 = 0X37;
/** READ_OCR - read the OCR register of a card */
uint8_t const CMD58 = 0X3A;
/** SET_WR_BLK_ERASE_COUNT - Set the number of write blocks to be
pre-erased before writing */
uint8_t const ACMD23 = 0X17;
/** SD_SEND_OP_COMD - Sends host capacity support information and
activates the card's initialization process */
uint8_t const ACMD41 = 0X29;
//------------------------------------------------------------------------------
/** status for card in the ready state */
uint8_t const R1_READY_STATE = 0X00;
/** status for card in the idle state */
uint8_t const R1_IDLE_STATE = 0X01;
/** status bit for illegal command */
uint8_t const R1_ILLEGAL_COMMAND = 0X04;
/** start data token for read or write single block*/
uint8_t const DATA_START_BLOCK = 0XFE;
/** stop token for write multiple blocks*/
uint8_t const STOP_TRAN_TOKEN = 0XFD;
/** start data token for write multiple blocks*/
uint8_t const WRITE_MULTIPLE_TOKEN = 0XFC;
/** mask for data response tokens after a write block operation */
uint8_t const DATA_RES_MASK = 0X1F;
/** write data accepted token */
uint8_t const DATA_RES_ACCEPTED = 0X05;
//------------------------------------------------------------------------------
typedef struct CID {
// byte 0
uint8_t mid; // Manufacturer ID
// byte 1-2
char oid[2]; // OEM/Application ID
// byte 3-7
char pnm[5]; // Product name
// byte 8
unsigned prv_m : 4; // Product revision n.m
unsigned prv_n : 4;
// byte 9-12
uint32_t psn; // Product serial number
// byte 13
unsigned mdt_year_high : 4; // Manufacturing date
unsigned reserved : 4;
// byte 14
unsigned mdt_month : 4;
unsigned mdt_year_low : 4;
// byte 15
unsigned always1 : 1;
unsigned crc : 7;
} cid_t;
//------------------------------------------------------------------------------
// CSD for version 1.00 cards
typedef struct CSDV1 {
// byte 0
unsigned reserved1 : 6;
unsigned csd_ver : 2;
// byte 1
uint8_t taac;
// byte 2
uint8_t nsac;
// byte 3
uint8_t tran_speed;
// byte 4
uint8_t ccc_high;
// byte 5
unsigned read_bl_len : 4;
unsigned ccc_low : 4;
// byte 6
unsigned c_size_high : 2;
unsigned reserved2 : 2;
unsigned dsr_imp : 1;
unsigned read_blk_misalign : 1;
unsigned write_blk_misalign : 1;
unsigned read_bl_partial : 1;
// byte 7
uint8_t c_size_mid;
// byte 8
unsigned vdd_r_curr_max : 3;
unsigned vdd_r_curr_min : 3;
unsigned c_size_low : 2;
// byte 9
unsigned c_size_mult_high : 2;
unsigned vdd_w_cur_max : 3;
unsigned vdd_w_curr_min : 3;
// byte 10
unsigned sector_size_high : 6;
unsigned erase_blk_en : 1;
unsigned c_size_mult_low : 1;
// byte 11
unsigned wp_grp_size : 7;
unsigned sector_size_low : 1;
// byte 12
unsigned write_bl_len_high : 2;
unsigned r2w_factor : 3;
unsigned reserved3 : 2;
unsigned wp_grp_enable : 1;
// byte 13
unsigned reserved4 : 5;
unsigned write_partial : 1;
unsigned write_bl_len_low : 2;
// byte 14
unsigned reserved5: 2;
unsigned file_format : 2;
unsigned tmp_write_protect : 1;
unsigned perm_write_protect : 1;
unsigned copy : 1;
unsigned file_format_grp : 1;
// byte 15
unsigned always1 : 1;
unsigned crc : 7;
} csd1_t;
//------------------------------------------------------------------------------
// CSD for version 2.00 cards
typedef struct CSDV2 {
// byte 0
unsigned reserved1 : 6;
unsigned csd_ver : 2;
// byte 1
uint8_t taac;
// byte 2
uint8_t nsac;
// byte 3
uint8_t tran_speed;
// byte 4
uint8_t ccc_high;
// byte 5
unsigned read_bl_len : 4;
unsigned ccc_low : 4;
// byte 6
unsigned reserved2 : 4;
unsigned dsr_imp : 1;
unsigned read_blk_misalign : 1;
unsigned write_blk_misalign : 1;
unsigned read_bl_partial : 1;
// byte 7
unsigned reserved3 : 2;
unsigned c_size_high : 6;
// byte 8
uint8_t c_size_mid;
// byte 9
uint8_t c_size_low;
// byte 10
unsigned sector_size_high : 6;
unsigned erase_blk_en : 1;
unsigned reserved4 : 1;
// byte 11
unsigned wp_grp_size : 7;
unsigned sector_size_low : 1;
// byte 12
unsigned write_bl_len_high : 2;
unsigned r2w_factor : 3;
unsigned reserved5 : 2;
unsigned wp_grp_enable : 1;
// byte 13
unsigned reserved6 : 5;
unsigned write_partial : 1;
unsigned write_bl_len_low : 2;
// byte 14
unsigned reserved7: 2;
unsigned file_format : 2;
unsigned tmp_write_protect : 1;
unsigned perm_write_protect : 1;
unsigned copy : 1;
unsigned file_format_grp : 1;
// byte 15
unsigned always1 : 1;
unsigned crc : 7;
} csd2_t;
//------------------------------------------------------------------------------
// union of old and new style CSD register
union csd_t {
csd1_t v1;
csd2_t v2;
};
#endif // SdInfo_h

View File

@ -0,0 +1,351 @@
/* Arduino SdFat Library
Copyright (C) 2009 by William Greiman
This file is part of the Arduino SdFat Library
This Library is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with the Arduino SdFat Library. If not, see
<http://www.gnu.org/licenses/>.
*/
#include "SdFat.h"
//------------------------------------------------------------------------------
// raw block cache
// init cacheBlockNumber_to invalid SD block number
uint32_t SdVolume::cacheBlockNumber_ = 0XFFFFFFFF;
cache_t SdVolume::cacheBuffer_; // 512 byte cache for Sd2Card
Sd2Card* SdVolume::sdCard_; // pointer to SD card object
uint8_t SdVolume::cacheDirty_ = 0; // cacheFlush() will write block if true
uint32_t SdVolume::cacheMirrorBlock_ = 0; // mirror block for second FAT
//------------------------------------------------------------------------------
// find a contiguous group of clusters
uint8_t SdVolume::allocContiguous(uint32_t count, uint32_t* curCluster) {
// start of group
uint32_t bgnCluster;
// flag to save place to start next search
uint8_t setStart;
// set search start cluster
if (*curCluster) {
// try to make file contiguous
bgnCluster = *curCluster + 1;
// don't save new start location
setStart = false;
} else {
// start at likely place for free cluster
bgnCluster = allocSearchStart_;
// save next search start if one cluster
setStart = 1 == count;
}
// end of group
uint32_t endCluster = bgnCluster;
// last cluster of FAT
uint32_t fatEnd = clusterCount_ + 1;
// search the FAT for free clusters
for (uint32_t n = 0;; n++, endCluster++) {
// can't find space checked all clusters
if (n >= clusterCount_) {
return false;
}
// past end - start from beginning of FAT
if (endCluster > fatEnd) {
bgnCluster = endCluster = 2;
}
uint32_t f;
if (!fatGet(endCluster, &f)) {
return false;
}
if (f != 0) {
// cluster in use try next cluster as bgnCluster
bgnCluster = endCluster + 1;
} else if ((endCluster - bgnCluster + 1) == count) {
// done - found space
break;
}
}
// mark end of chain
if (!fatPutEOC(endCluster)) {
return false;
}
// link clusters
while (endCluster > bgnCluster) {
if (!fatPut(endCluster - 1, endCluster)) {
return false;
}
endCluster--;
}
if (*curCluster != 0) {
// connect chains
if (!fatPut(*curCluster, bgnCluster)) {
return false;
}
}
// return first cluster number to caller
*curCluster = bgnCluster;
// remember possible next free cluster
if (setStart) {
allocSearchStart_ = bgnCluster + 1;
}
return true;
}
//------------------------------------------------------------------------------
uint8_t SdVolume::cacheFlush(uint8_t blocking) {
if (cacheDirty_) {
if (!sdCard_->writeBlock(cacheBlockNumber_, cacheBuffer_.data, blocking)) {
return false;
}
if (!blocking) {
return true;
}
// mirror FAT tables
if (!cacheMirrorBlockFlush(blocking)) {
return false;
}
cacheDirty_ = 0;
}
return true;
}
//------------------------------------------------------------------------------
uint8_t SdVolume::cacheMirrorBlockFlush(uint8_t blocking) {
if (cacheMirrorBlock_) {
if (!sdCard_->writeBlock(cacheMirrorBlock_, cacheBuffer_.data, blocking)) {
return false;
}
cacheMirrorBlock_ = 0;
}
return true;
}
//------------------------------------------------------------------------------
uint8_t SdVolume::cacheRawBlock(uint32_t blockNumber, uint8_t action) {
if (cacheBlockNumber_ != blockNumber) {
if (!cacheFlush()) {
return false;
}
if (!sdCard_->readBlock(blockNumber, cacheBuffer_.data)) {
return false;
}
cacheBlockNumber_ = blockNumber;
}
cacheDirty_ |= action;
return true;
}
//------------------------------------------------------------------------------
// cache a zero block for blockNumber
uint8_t SdVolume::cacheZeroBlock(uint32_t blockNumber) {
if (!cacheFlush()) {
return false;
}
// loop take less flash than memset(cacheBuffer_.data, 0, 512);
for (uint16_t i = 0; i < 512; i++) {
cacheBuffer_.data[i] = 0;
}
cacheBlockNumber_ = blockNumber;
cacheSetDirty();
return true;
}
//------------------------------------------------------------------------------
// return the size in bytes of a cluster chain
uint8_t SdVolume::chainSize(uint32_t cluster, uint32_t* size) const {
uint32_t s = 0;
do {
if (!fatGet(cluster, &cluster)) {
return false;
}
s += 512UL << clusterSizeShift_;
} while (!isEOC(cluster));
*size = s;
return true;
}
//------------------------------------------------------------------------------
// Fetch a FAT entry
uint8_t SdVolume::fatGet(uint32_t cluster, uint32_t* value) const {
if (cluster > (clusterCount_ + 1)) {
return false;
}
uint32_t lba = fatStartBlock_;
lba += fatType_ == 16 ? cluster >> 8 : cluster >> 7;
if (lba != cacheBlockNumber_) {
if (!cacheRawBlock(lba, CACHE_FOR_READ)) {
return false;
}
}
if (fatType_ == 16) {
*value = cacheBuffer_.fat16[cluster & 0XFF];
} else {
*value = cacheBuffer_.fat32[cluster & 0X7F] & FAT32MASK;
}
return true;
}
//------------------------------------------------------------------------------
// Store a FAT entry
uint8_t SdVolume::fatPut(uint32_t cluster, uint32_t value) {
// error if reserved cluster
if (cluster < 2) {
return false;
}
// error if not in FAT
if (cluster > (clusterCount_ + 1)) {
return false;
}
// calculate block address for entry
uint32_t lba = fatStartBlock_;
lba += fatType_ == 16 ? cluster >> 8 : cluster >> 7;
if (lba != cacheBlockNumber_) {
if (!cacheRawBlock(lba, CACHE_FOR_READ)) {
return false;
}
}
// store entry
if (fatType_ == 16) {
cacheBuffer_.fat16[cluster & 0XFF] = value;
} else {
cacheBuffer_.fat32[cluster & 0X7F] = value;
}
cacheSetDirty();
// mirror second FAT
if (fatCount_ > 1) {
cacheMirrorBlock_ = lba + blocksPerFat_;
}
return true;
}
//------------------------------------------------------------------------------
// free a cluster chain
uint8_t SdVolume::freeChain(uint32_t cluster) {
// clear free cluster location
allocSearchStart_ = 2;
do {
uint32_t next;
if (!fatGet(cluster, &next)) {
return false;
}
// free cluster
if (!fatPut(cluster, 0)) {
return false;
}
cluster = next;
} while (!isEOC(cluster));
return true;
}
//------------------------------------------------------------------------------
/**
Initialize a FAT volume.
\param[in] dev The SD card where the volume is located.
\param[in] part The partition to be used. Legal values for \a part are
1-4 to use the corresponding partition on a device formatted with
a MBR, Master Boot Record, or zero if the device is formatted as
a super floppy with the FAT boot sector in block zero.
\return The value one, true, is returned for success and
the value zero, false, is returned for failure. Reasons for
failure include not finding a valid partition, not finding a valid
FAT file system in the specified partition or an I/O error.
*/
uint8_t SdVolume::init(Sd2Card* dev, uint8_t part) {
uint32_t volumeStartBlock = 0;
sdCard_ = dev;
// if part == 0 assume super floppy with FAT boot sector in block zero
// if part > 0 assume mbr volume with partition table
if (part) {
if (part > 4) {
return false;
}
if (!cacheRawBlock(volumeStartBlock, CACHE_FOR_READ)) {
return false;
}
part_t* p = &cacheBuffer_.mbr.part[part - 1];
if ((p->boot & 0X7F) != 0 ||
p->totalSectors < 100 ||
p->firstSector == 0) {
// not a valid partition
return false;
}
volumeStartBlock = p->firstSector;
}
if (!cacheRawBlock(volumeStartBlock, CACHE_FOR_READ)) {
return false;
}
bpb_t* bpb = &cacheBuffer_.fbs.bpb;
if (bpb->bytesPerSector != 512 ||
bpb->fatCount == 0 ||
bpb->reservedSectorCount == 0 ||
bpb->sectorsPerCluster == 0) {
// not valid FAT volume
return false;
}
fatCount_ = bpb->fatCount;
blocksPerCluster_ = bpb->sectorsPerCluster;
// determine shift that is same as multiply by blocksPerCluster_
clusterSizeShift_ = 0;
while (blocksPerCluster_ != (1 << clusterSizeShift_)) {
// error if not power of 2
if (clusterSizeShift_++ > 7) {
return false;
}
}
blocksPerFat_ = bpb->sectorsPerFat16 ?
bpb->sectorsPerFat16 : bpb->sectorsPerFat32;
fatStartBlock_ = volumeStartBlock + bpb->reservedSectorCount;
// count for FAT16 zero for FAT32
rootDirEntryCount_ = bpb->rootDirEntryCount;
// directory start for FAT16 dataStart for FAT32
rootDirStart_ = fatStartBlock_ + bpb->fatCount * blocksPerFat_;
// data start for FAT16 and FAT32
dataStartBlock_ = rootDirStart_ + ((32 * bpb->rootDirEntryCount + 511) / 512);
// total blocks for FAT16 or FAT32
uint32_t totalBlocks = bpb->totalSectors16 ?
bpb->totalSectors16 : bpb->totalSectors32;
// total data blocks
clusterCount_ = totalBlocks - (dataStartBlock_ - volumeStartBlock);
// divide by cluster size to get cluster count
clusterCount_ >>= clusterSizeShift_;
// FAT type is determined by cluster count
if (clusterCount_ < 4085) {
fatType_ = 12;
} else if (clusterCount_ < 65525) {
fatType_ = 16;
} else {
rootDirStart_ = bpb->fat32RootCluster;
fatType_ = 32;
}
return true;
}

861
libraries/SX1278FSK/SX1278FSK.cpp Executable file
View File

@ -0,0 +1,861 @@
/*
* Functions for using SX127x in FSK mode (mainly receive)
* Copyright (C) 2019 Hansi Reiser, dl9rdz
*
* Partially based on the SX1278 libraray for managing Semtech modules
* Copyright (C) 2015 Wireless Open Source
* http://wirelessopensource.com
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#include "SX1278FSK.h"
#include "SPI.h"
#include <Sonde.h>
#include <Display.h>
SX1278FSK::SX1278FSK()
{
// Initialize class variables
};
static SPISettings spiset = SPISettings(40000000L, MSBFIRST, SPI_MODE0);
/*
Function: Turns the module ON.
Returns: 0 on success, 1 otherwise
*/
uint8_t SX1278FSK::ON()
{
uint8_t state = 2;
#if (SX1278FSK_debug_mode > 1)
Serial.println();
Serial.println(F("Starting 'ON'"));
#endif
// Powering the module
pinMode(SX1278_SS, OUTPUT);
digitalWrite(SX1278_SS, HIGH);
//Configure the MISO, MOSI, CS, SPCR.
SPI.begin();
//Set most significant bit first
SPI.setBitOrder(MSBFIRST);
//Divide the clock frequency
SPI.setClockDivider(SPI_CLOCK_DIV2);
//Set data mode
SPI.setDataMode(SPI_MODE0);
// Set Maximum Over Current Protection
state = setMaxCurrent(0x1B);
if( state == 0 )
{
#if (SX1278FSK_debug_mode > 1)
Serial.println(F("## Setting ON with maximum current supply ##"));
Serial.println();
#endif
}
else
{
return 1;
}
// set FSK mode
state = setFSK();
return state;
}
/*
Function: Turn the module OFF.
Returns: Nothing
*/
void SX1278FSK::OFF()
{
#if (SX1278FSK_debug_mode > 1)
Serial.println();
Serial.println(F("Starting 'OFF'"));
#endif
SPI.end();
// Powering the module
pinMode(SX1278_SS,OUTPUT);
digitalWrite(SX1278_SS,LOW);
#if (SX1278FSK_debug_mode > 1)
Serial.println(F("## Setting OFF ##"));
Serial.println();
#endif
}
/*
Function: Reads the indicated register.
Returns: The content of the register
Parameters:
address: address register to read from
*/
byte SX1278FSK::readRegister(byte address)
{
byte value = 0x00;
SPI.beginTransaction(spiset);
digitalWrite(SX1278_SS,LOW);
//delay(1);
bitClear(address, 7); // Bit 7 cleared to write in registers
SPI.transfer(address);
value = SPI.transfer(0x00);
digitalWrite(SX1278_SS,HIGH);
SPI.endTransaction();
#if (SX1278FSK_debug_mode > 1)
if(address!=0x3F) {
Serial.print(F("## Reading: ##\t"));
Serial.print(F("Register "));
Serial.print(address, HEX);
Serial.print(F(": "));
Serial.print(value, HEX);
Serial.println();
}
#endif
return value;
}
/*
Function: Writes on the indicated register.
Returns: Nothing
Parameters:
address: address register to write in
data: value to write in the register
*/
void SX1278FSK::writeRegister(byte address, byte data)
{
SPI.beginTransaction(spiset);
digitalWrite(SX1278_SS,LOW);
//delay(1);
bitSet(address, 7); // Bit 7 set to read from registers
SPI.transfer(address);
SPI.transfer(data);
digitalWrite(SX1278_SS,HIGH);
SPI.endTransaction();
#if (SX1278FSK_debug_mode > 1)
Serial.print(F("## Writing: ##\t"));
Serial.print(F("Register "));
bitClear(address, 7);
Serial.print(address, HEX);
Serial.print(F(": "));
Serial.print(data, HEX);
Serial.println();
#endif
}
/*
* Function: Clears the IRQ flags
*
* Configuration registers are accessed through the SPI interface.
* Registers are readable in all device mode including Sleep. However, they
* should be written only in Sleep and Stand-by modes.
*
* Returns: Nothing
*/
void SX1278FSK::clearIRQFlags()
{
byte st0;
// Save the previous status
st0 = readRegister(REG_OP_MODE);
// Stdby mode to write in registers
writeRegister(REG_OP_MODE, FSK_STANDBY_MODE);
// FSK mode flags1 register
writeRegister(REG_IRQ_FLAGS1, 0xFF);
// FSK mode flags2 register
writeRegister(REG_IRQ_FLAGS2, 0xFF);
// Getting back to previous status
if(st0 != FSK_STANDBY_MODE) {
writeRegister(REG_OP_MODE, st0);
}
#if (SX1278FSK_debug_mode > 1)
Serial.println(F("## FSK flags cleared ##"));
#endif
}
/*
Function: Sets the module in FSK mode.
Returns: Integer that determines if there has been any error
state = 2 --> The command has not been executed
state = 1 --> There has been an error while executing the command
state = 0 --> The command has been executed with no errors
*/
uint8_t SX1278FSK::setFSK()
{
uint8_t state = 2;
byte st0;
byte config1;
#if (SX1278FSK_debug_mode > 1)
Serial.println();
Serial.println(F("Starting 'setFSK'"));
#endif
writeRegister(REG_OP_MODE, FSK_SLEEP_MODE); // Sleep mode (mandatory to change mode)
// If we are in LORA mode, above line activate Sleep mode, but does not change mode to FSK
// as mode change is only allowed in sleep mode. Next line changes to FSK
writeRegister(REG_OP_MODE, FSK_SLEEP_MODE);
writeRegister(REG_OP_MODE, FSK_STANDBY_MODE); // FSK standby mode
//writeRegister(REG_FIFO_THRESH, 0x80); // condition to start packet tx
//config1 = readRegister(REG_SYNC_CONFIG);
//config1 = config1 & B00111111;
//writeRegister(REG_SYNC_CONFIG,config1);
delay(100);
st0 = readRegister(REG_OP_MODE); // Reading config mode
if( st0 == FSK_STANDBY_MODE )
{ // FSK mode
state = 0;
#if (SX1278FSK_debug_mode > 1)
Serial.println(F("## FSK set with success ##"));
Serial.println();
#endif
} else { // LoRa mode
state = 1;
Serial.println( st0 );
#if (SX1278FSK_debug_mode > 1)
Serial.println(F("** There has been an error while setting FSK **"));
Serial.println();
#endif
}
return state;
}
/* Function: Sets FSK bitrate
* Returns: 0 for success, >0 in case of error
* Parameters: bps: requested bitrate
* (raw data rate, for Mancester encoding, the effective bitrate is bps/2)
*/
uint8_t SX1278FSK::setBitrate(float bps)
{
// TODO: Check if FSK mode is active
// check if bitrate is allowed allowed bitrate
if((bps < 1200) || (bps > 300000)) {
return 1;
}
// set mode to FSK STANDBY
writeRegister(REG_OP_MODE, FSK_STANDBY_MODE);
// set bit rate
uint16_t bitRate = (SX127X_CRYSTAL_FREQ * 1.0) / bps;
writeRegister(REG_BITRATE_MSB, (bitRate & 0xFF00) >> 8);
writeRegister(REG_BITRATE_LSB, (bitRate & 0x00FF));
// also set fractional part
uint16_t fracRate = (SX127X_CRYSTAL_FREQ * 16.0) / bps - bitRate * 16 + 0.5;
writeRegister(REG_BIT_RATE_FRAC, fracRate&0x0F);
return 0;
}
/* Function: Gets configured bitrate
* Returns bitrate in bit/second
*/
float SX1278FSK::getBitrate()
{
uint8_t fmsb = readRegister(REG_BITRATE_MSB);
uint8_t flsb = readRegister(REG_BITRATE_LSB);
uint8_t ffrac = readRegister(REG_BIT_RATE_FRAC) & 0x0F;
return SX127X_CRYSTAL_FREQ / ( (fmsb<<8) + flsb + ffrac / 16.0 );
}
//typedef struct rxbwset { float bw; uint8_t mant; uint8_t rxp; } st_rxbwsettings;
uint8_t SX1278FSK::setRxBandwidth(float bw)
{
// TODO: Check if in FSK mode
//
if(bw<2600 || bw>250000) { return 1; /* invalid */ }
uint8_t rxbwexp = 1;
bw = SX127X_CRYSTAL_FREQ / bw / 8;
while(bw>31) { rxbwexp++; bw/=2.0; }
uint8_t rxbwmant = bw<17?0 : bw<21? 1:2;
// set mode to FSK STANDBY
writeRegister(REG_OP_MODE, FSK_STANDBY_MODE);
writeRegister(REG_RX_BW, rxbwexp | (rxbwmant<<3));
return 0;
}
float SX1278FSK::getRxBandwidth()
{
uint8_t rxbw = readRegister(REG_RX_BW);
uint8_t rxbwexp = rxbw&0x07;
uint8_t rxbwmant = (rxbw>>3)&0x03;
rxbwmant = 16 + 4*rxbwmant;
return SX127X_CRYSTAL_FREQ / ( rxbwmant << (rxbwexp+2));
}
uint8_t SX1278FSK::setAFCBandwidth(float bw)
{
// TODO: Check if in FSK mode
//
if(bw<2600 || bw>250000) { return 1; /* invalid */ }
uint8_t rxbwexp = 1;
bw = SX127X_CRYSTAL_FREQ / bw / 8;
while(bw>31) { rxbwexp++; bw/=2.0; }
uint8_t rxbwmant = bw<17?0 : bw<21? 1:2;
// set mode to FSK STANDBY
writeRegister(REG_OP_MODE, FSK_STANDBY_MODE);
writeRegister(REG_AFC_BW, rxbwexp | (rxbwmant<<3));
return 0;
}
float SX1278FSK::getAFCBandwidth()
{
uint8_t rxbw = readRegister(REG_AFC_BW);
uint8_t rxbwexp = rxbw&0x07;
uint8_t rxbwmant = (rxbw>>3)&0x03;
rxbwmant = 16 + 4*rxbwmant;
return SX127X_CRYSTAL_FREQ / ( rxbwmant << (rxbwexp+2));
}
uint8_t SX1278FSK::setFrequency(float freq) {
// set mode to FSK STANDBY
writeRegister(REG_OP_MODE, FSK_STANDBY_MODE);
freq += sonde.config.freqofs; // manual frequency correction
uint32_t frf = freq * 1.0 * (1<<19) / SX127X_CRYSTAL_FREQ;
writeRegister(REG_FRF_MSB, (frf&0xff0000)>>16);
writeRegister(REG_FRF_MID, (frf&0x00ff00)>>8);
writeRegister(REG_FRF_LSB, (frf&0x0000ff));
return 0;
}
float SX1278FSK::getFrequency() {
uint8_t fmsb = readRegister(REG_FRF_MSB);
uint8_t fmid = readRegister(REG_FRF_MID);
uint8_t flsb = readRegister(REG_FRF_LSB);
return ((fmsb<<16)|(fmid<<8)|flsb) * 1.0 / (1<<19) * SX127X_CRYSTAL_FREQ;
}
static int gaintab[]={-999,0,-6,-12,-24,-36,-48,-999};
int SX1278FSK::getLNAGain() {
int gain = (readRegister(REG_LNA)>>5)&0x07;
return gaintab[gain];
}
uint8_t SX1278FSK::setLNAGain(int gain) {
uint8_t g=1;
while(gain<gaintab[g] && g<6) {g++; }
writeRegister(REG_LNA, g<<5);
return 0;
}
uint8_t SX1278FSK::getRxConf() {
return readRegister(REG_RX_CONFIG);
}
uint8_t SX1278FSK::setRxConf(uint8_t conf) {
writeRegister(REG_RX_CONFIG, conf);
return 0;
}
uint8_t SX1278FSK::setSyncConf(uint8_t conf, int len, const uint8_t *syncpattern) {
int res=0;
writeRegister(REG_SYNC_CONFIG, conf);
if(len>8) return 1;
for(int i=0; i<len; i++) {
writeRegister(REG_SYNC_VALUE1+i, syncpattern[i]);
}
return res;
}
uint8_t SX1278FSK::getSyncConf() {
return sx1278.readRegister(REG_SYNC_CONFIG);
}
uint8_t SX1278FSK::setPreambleDetect(uint8_t conf) {
sx1278.writeRegister(REG_PREAMBLE_DETECT, conf);
return 0;
}
uint8_t SX1278FSK::getPreambleDetect() {
return sx1278.readRegister(REG_PREAMBLE_DETECT);
}
uint8_t SX1278FSK::setPacketConfig(uint8_t conf1, uint8_t conf2)
{
uint8_t ret=0;
sx1278.writeRegister(REG_PACKET_CONFIG1, conf1);
sx1278.writeRegister(REG_PACKET_CONFIG2, conf2);
return ret;
};
uint16_t SX1278FSK::getPacketConfig() {
uint8_t c1 = sx1278.readRegister(REG_PACKET_CONFIG1);
uint8_t c2 = sx1278.readRegister(REG_PACKET_CONFIG2);
return (c2<<8)|c1;
}
/*
Function: Gets the preamble length from the module.
Returns: preamble length
*/
uint16_t SX1278FSK::getPreambleLength()
{
uint16_t p_length;
#if (SX1278FSK_debug_mode > 1)
Serial.println();
Serial.println(F("Starting 'getPreambleLength'"));
#endif
p_length = readRegister(REG_PREAMBLE_MSB_FSK);
p_length = (p_length<<8) | readRegister(REG_PREAMBLE_LSB_FSK);
#if (SX1278FSK_debug_mode > 1)
Serial.print(F("## Preamble length configured is "));
Serial.print(p_length, HEX);
Serial.print(F(" ##"));
Serial.println();
#endif
return p_length;
}
/*
Function: Sets the preamble length in the module
Returns: Integer that determines if there has been any error
state = 2 --> The command has not been executed
state = 1 --> There has been an error while executing the command
state = 0 --> The command has been executed with no errors
Parameters:
l: length value to set as preamble length.
*/
uint8_t SX1278FSK::setPreambleLength(uint16_t l)
{
byte st0;
int8_t state = 2;
#if (SX1278FSK_debug_mode > 1)
Serial.println();
Serial.println(F("Starting 'setPreambleLength'"));
#endif
st0 = readRegister(REG_OP_MODE); // Save the previous status
writeRegister(REG_OP_MODE, FSK_STANDBY_MODE); // Set Standby mode to write in registers
// Storing MSB preamble length in FSK mode
writeRegister(REG_PREAMBLE_MSB_FSK, l>>8);
writeRegister(REG_PREAMBLE_LSB_FSK, l&0xFF);
state = 0;
#if (SX1278FSK_debug_mode > 1)
Serial.print(F("## Preamble length "));
Serial.print(l, HEX);
Serial.println(F(" has been successfully set ##"));
Serial.println();
#endif
if(st0 != FSK_STANDBY_MODE) {
writeRegister(REG_OP_MODE, st0); // Getting back to previous status
}
return state;
}
/*
Function: Gets the payload length from the module.
Returns: configured length; -1 on error
*/
int SX1278FSK::getPayloadLength()
{
int length;
#if (SX1278FSK_debug_mode > 1)
Serial.println();
Serial.println(F("Starting 'getPayloadLength'"));
#endif
length = readRegister(REG_PAYLOAD_LENGTH_FSK);
#if (SX1278FSK_debug_mode > 1)
Serial.print(F("## Payload length configured is "));
Serial.print(length);
Serial.println(F(" ##"));
#endif
return length;
}
/*
Function: Sets the payload length from the module.
Returns: 0 for ok, otherwise error
// TODO: Larger than 255 bytes?
*/
uint8_t SX1278FSK::setPayloadLength(int len)
{
#if (SX1278FSK_debug_mode > 1)
Serial.print(F("Starting 'setPayloadLength'"));
Serial.println(len);
#endif
uint8_t conf2 = readRegister(REG_PACKET_CONFIG2);
conf2 = (conf2 & 0xF8) | ( (len>>8)&0x7 );
writeRegister(REG_PACKET_CONFIG2, conf2);
writeRegister(REG_PAYLOAD_LENGTH_FSK, len&0xFF);
return 0;
}
/*
Function: Gets the current value of RSSI.
Returns: RSSI value
*/
int16_t SX1278FSK::getRSSI()
{
int16_t RSSI;
//int rssi_mean = 0;
int total = 1;
/// FSK mode
// get mean value of RSSI
for(int i = 0; i < total; i++)
{
RSSI = readRegister(REG_RSSI_VALUE_FSK);
//rssi_mean += _RSSI;
}
//rssi_mean = rssi_mean / total;
//RSSI = rssi_mean;
#if (SX1278FSK_debug_mode > 0)
Serial.print(F("## RSSI value is "));
Serial.print(RSSI);
Serial.println(F(" ##"));
#endif
return RSSI;
}
/*
Function: Gets the current value of FEI (frequency error indication)
Returns: FEI value in Hz
*/
int32_t SX1278FSK::getFEI()
{
int32_t FEI;
int16_t regval = (readRegister(REG_FEI_MSB)<<8) | readRegister(REG_FEI_LSB);
//Serial.printf("feireg: %04x\n", regval);
FEI = (int32_t)(regval * SX127X_FSTEP);
return FEI;
}
/*
Function: Gets the current value of AFC (automated frequency correction)
Returns: AFC value in Hz
*/
int32_t SX1278FSK::getAFC()
{
int32_t AFC;
int16_t regval = (readRegister(REG_AFC_MSB)<<8) | readRegister(REG_AFC_LSB);
//Serial.printf("afcreg: %04x\n", regval);
AFC = (int32_t)(regval * SX127X_FSTEP);
return AFC;
}
/*
Function: Gets the current supply limit of the power amplifier, protecting battery chemistries.
Returns: Integer that determines if there has been any error
state = 2 --> The command has not been executed
state = 1 --> There has been an error while executing the command
state = 0 --> The command has been executed with no errors
Parameters:
rate: value to compute the maximum current supply. Maximum current is 45+5*'rate' [mA]
*/
int SX1278FSK::getMaxCurrent()
{
int value;
#if (SX1278FSK_debug_mode > 1)
Serial.println();
Serial.println(F("Starting 'getMaxCurrent'"));
#endif
value = readRegister(REG_OCP);
// extract only the OcpTrim value from the OCP register
value &= B00011111;
if( value <= 15 ) {
value = (45 + (5 * value));
} else if( value <= 27 ) {
value = (-30 + (10 * value));
} else {
value = 240;
}
#if (SX1278FSK_debug_mode > 1)
Serial.print(F("## Maximum current supply configured is "));
Serial.print(value, DEC);
Serial.println(F(" mA ##"));
Serial.println();
#endif
return value;
}
/*
Function: Limits the current supply of the power amplifier, protecting battery chemistries.
Returns: Integer that determines if there has been any error
state = 2 --> The command has not been executed
state = 1 --> There has been an error while executing the command
state = 0 --> The command has been executed with no errors
state = -1 --> Forbidden parameter value for this function
Parameters:
rate: value to compute the maximum current supply. Range: 0x00 to 0x1B. The
Maximum current is:
Imax = 45+5*OcpTrim [mA] if OcpTrim <= 15 (120 mA) /
Imax = -30+10*OcpTrim [mA] if 15 < OcpTrim <= 27 (130 to 240 mA)
Imax = 240mA for higher settings
*/
int8_t SX1278FSK::setMaxCurrent(uint8_t rate)
{
int8_t state = 2;
byte st0;
#if (SX1278FSK_debug_mode > 1)
Serial.println();
Serial.println(F("Starting 'setMaxCurrent'"));
#endif
// Maximum rate value = 0x1B, because maximum current supply = 240 mA
if (rate > 0x1B)
{
state = -1;
#if (SX1278FSK_debug_mode > 1)
Serial.print(F("** Maximum current supply is 240 mA, "));
Serial.println(F("so maximum parameter value must be 27 (DEC) or 0x1B (HEX) **"));
Serial.println();
#endif
}
else
{
// Enable Over Current Protection
rate |= B00100000;
state = 1;
st0 = readRegister(REG_OP_MODE); // Save the previous status
writeRegister(REG_OP_MODE, FSK_STANDBY_MODE); // Set FSK Standby mode to write in registers
writeRegister(REG_OCP, rate); // Modifying maximum current supply
if(st0 != FSK_STANDBY_MODE) {
writeRegister(REG_OP_MODE, st0); // Getting back to previous status
}
state = 0;
}
return state;
}
/*
Function: Configures the module to receive information.
Returns: Integer that determines if there has been any error
state = 2 --> The command has not been executed
state = 1 --> There has been an error while executing the command
state = 0 --> The command has been executed with no errors
*/
uint8_t SX1278FSK::receive()
{
uint8_t state = 1;
#if (SX1278FSK_debug_mode > 1)
Serial.println();
Serial.println(F("Starting 'receive'"));
#endif
// TODO: Is there anything else to be done?
//
writeRegister(REG_OP_MODE, FSK_RX_MODE);
state = 0;
#if (SX1278FSK_debug_mode > 1)
Serial.println(F("## Receiving FSK mode activated with success ##"));
#endif
return state;
}
/*
Function: Configures the module to receive a packet
Returns: Integer that determines if there has been any error
state = 2 --> The command has not been executed
state = 1 --> There has been an error while executing the command
state = 0 --> The command has been executed with no errors
Parameters:
wait: timeout in ms
data: memory where to place received data
*/
uint8_t SX1278FSK::receivePacketTimeout(uint32_t wait, byte *data)
{
int di=0;
uint8_t state = 2;
unsigned long previous;
byte value = 0x00;
#if (SX1278FSK_debug_mode > 1)
Serial.println();
Serial.println(F("Starting 'receivePacketTimeout'"));
#endif
// set RX mode
state = receive();
if(state != 0) { return state; }
#if (SX1278FSK_debug_mode > 0)
Serial.println(F("RX mode sucessfully activated"));
#endif
previous = millis();
/// FSK mode
value = readRegister(REG_IRQ_FLAGS2);
byte ready=0;
// while not yet done or FIFO not yet empty
while( (!ready || bitRead(value,6)==0) && (millis() - previous < wait) )
{
if( bitRead(value,2)==1 ) ready=1;
if( bitRead(value, 6) == 0 ) { // FIFO not empty
data[di++] = readRegister(REG_FIFO);
// It's a bit of a hack.... get RSSI and AFC (a) at beginning of packet and
// for RS41 after about 0.5 sec. It might be more logical to put this decoder-specific
// code into RS41.cpp instead of this file... (maybe TODO?)
if(di==1 || di==290 ) {
int rssi=getRSSI();
int afc=getAFC();
#if 0
Serial.printf("Test(%d): RSSI=%d", rxtask.currentSonde, rssi/2);
Serial.print("Test: AFC="); Serial.println(afc);
#endif
sonde.sondeList[rxtask.currentSonde].rssi = rssi;
sonde.sondeList[rxtask.currentSonde].afc = afc;
if(rxtask.receiveResult==0xFFFF)
rxtask.receiveResult = RX_UPDATERSSI;
//sonde.si()->rssi = rssi;
//sonde.si()->afc = afc;
}
if(di>520) {
// TODO
Serial.println("TOO MUCH DATA");
break;
}
previous = millis(); // reset timeout after receiving data
} else {
delay(10);
}
value = readRegister(REG_IRQ_FLAGS2);
}
if( !ready || bitRead(value, 6)==0) {
#if 1&&(SX1278FSK_debug_mode > 0)
Serial.println(F("** The timeout has expired **"));
Serial.println();
#endif
sonde.si()->rssi = getRSSI();
writeRegister(REG_OP_MODE, FSK_STANDBY_MODE); // Setting standby FSK mode
return 1; // TIMEOUT
}
#if (SX1278FSK_debug_mode > 0)
Serial.println(F("## Packet received:"));
for(unsigned int i = 0; i < di; i++)
{
Serial.print(data[i], HEX); // Printing payload
Serial.print("|");
}
Serial.println(F(" ##"));
#endif
state = 0;
// Initializing flags
clearIRQFlags();
return state;
}
#if 0
/*
Function: It gets the temperature from the measurement block module.
Returns: Integer that determines if there has been any error
state = 2 --> The command has not been executed
state = 1 --> There has been an error while executing the command
state = 0 --> The command has been executed with no errors
*/
uint8_t SX1278FSK::getTemp()
{
byte st0;
uint8_t state = 2;
#if (SX1278FSK_debug_mode > 1)
Serial.println();
Serial.println(F("Starting 'getTemp'"));
#endif
st0 = readRegister(REG_OP_MODE); // Save the previous status
if( _modem == LORA )
{ // Allowing access to FSK registers while in LoRa standby mode
writeRegister(REG_OP_MODE, LORA_STANDBY_FSK_REGS_MODE);
}
state = 1;
// Saving temperature value
_temp = readRegister(REG_TEMP);
if( _temp & 0x80 ) // The SNR sign bit is 1
{
// Invert and divide by 4
_temp = ( ( ~_temp + 1 ) & 0xFF );
}
else
{
// Divide by 4
_temp = ( _temp & 0xFF );
}
#if (SX1278FSK_debug_mode > 1)
Serial.print(F("## Temperature is: "));
Serial.print(_temp);
Serial.println(F(" ##"));
Serial.println();
#endif
if( _modem == LORA )
{
writeRegister(REG_OP_MODE, st0); // Getting back to previous status
}
state = 0;
return state;
}
/*
Function: It prints the registers related to RX
Returns: Integer that determines if there has been any error
state = 2 --> The command has not been executed
state = 1 --> There has been an error while executing the command
state = 0 --> The command has been executed with no errors
*/
void SX1278FSK::showRxRegisters()
{
Serial.println(F("\n--- Show RX register ---"));
// variable
byte reg;
for(int i = 0x00; i < 0x80; i++)
{
reg = readRegister(i);
Serial.print(F("Reg 0x"));
Serial.print(i, HEX);
Serial.print(F(":"));
Serial.print(reg, HEX);
Serial.println();
delay(100);
}
Serial.println(F("------------------------"));
}
#endif
SX1278FSK sx1278 = SX1278FSK();

278
libraries/SX1278FSK/SX1278FSK.h Executable file
View File

@ -0,0 +1,278 @@
/*
* Functions for using SX127x in FSK mode (mainly receive)
* Copyright (C) 2019 Hansi Reiser, dl9rdz
*
* Partially based on the SX1278 libraray for managing Semtech modules
* Copyright (C) 2015 Wireless Open Source
* http://wirelessopensource.com
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#ifndef SX1278FSK_h
#define SX1278FSK_h
/******************************************************************************
* Includes
******************************************************************************/
#include <stdlib.h>
#include <stdint.h>
#include <Arduino.h>
#include <SPI.h>
#ifndef inttypes_h
#include <inttypes.h>
#endif
/******************************************************************************
* Definitions & Declarations
*****************************************************************************/
#define SX127X_CRYSTAL_FREQ 32000000
#define SX127X_FSTEP (SX127X_CRYSTAL_FREQ*1.0/(1<<19))
#define SX1278FSK_debug_mode 0
#define SX1278_SS SS
//! MACROS //
#define bitRead(value, bit) (((value) >> (bit)) & 0x01) // read a bit
#define bitSet(value, bit) ((value) |= (1UL << (bit))) // set bit to '1'
#define bitClear(value, bit) ((value) &= ~(1UL << (bit))) // set bit to '0'
//! REGISTERS //
// FSK Commun LORA
#define REG_FIFO 0x00
#define REG_OP_MODE 0x01
#define REG_BITRATE_MSB 0x02
#define REG_BITRATE_LSB 0x03
#define REG_FDEV_MSB 0x04
#define REG_FDEV_LSB 0x05
#define REG_FRF_MSB 0x06
#define REG_FRF_MID 0x07
#define REG_FRF_LSB 0x08
#define REG_PA_CONFIG 0x09
#define REG_PA_RAMP 0x0A
#define REG_OCP 0x0B
#define REG_LNA 0x0C
#define REG_RX_CONFIG 0x0D
#define REG_FIFO_ADDR_PTR 0x0D
#define REG_RSSI_CONFIG 0x0E
#define REG_FIFO_TX_BASE_ADDR 0x0E
#define REG_RSSI_COLLISION 0x0F
#define REG_FIFO_RX_BASE_ADDR 0x0F
#define REG_RSSI_THRESH 0x10
#define REG_FIFO_RX_CURRENT_ADDR 0x10
#define REG_RSSI_VALUE_FSK 0x11
#define REG_IRQ_FLAGS_MASK 0x11
#define REG_RX_BW 0x12
#define REG_IRQ_FLAGS 0x12
#define REG_AFC_BW 0x13
#define REG_RX_NB_BYTES 0x13
#define REG_OOK_PEAK 0x14
#define REG_RX_HEADER_CNT_VALUE_MSB 0x14
#define REG_OOK_FIX 0x15
#define REG_RX_HEADER_CNT_VALUE_LSB 0x15
#define REG_OOK_AVG 0x16
#define REG_RX_PACKET_CNT_VALUE_MSB 0x16
#define REG_RX_PACKET_CNT_VALUE_LSB 0x17
#define REG_MODEM_STAT 0x18
#define REG_PKT_SNR_VALUE 0x19
#define REG_AFC_FEI 0x1A
#define REG_PKT_RSSI_VALUE 0x1A
#define REG_AFC_MSB 0x1B
#define REG_RSSI_VALUE_LORA 0x1B
#define REG_AFC_LSB 0x1C
#define REG_HOP_CHANNEL 0x1C
#define REG_FEI_MSB 0x1D
#define REG_MODEM_CONFIG1 0x1D
#define REG_FEI_LSB 0x1E
#define REG_MODEM_CONFIG2 0x1E
#define REG_PREAMBLE_DETECT 0x1F
#define REG_SYMB_TIMEOUT_LSB 0x1F
#define REG_RX_TIMEOUT1 0x20
#define REG_PREAMBLE_MSB_LORA 0x20
#define REG_RX_TIMEOUT2 0x21
#define REG_PREAMBLE_LSB_LORA 0x21
#define REG_RX_TIMEOUT3 0x22
#define REG_PAYLOAD_LENGTH_LORA 0x22
#define REG_RX_DELAY 0x23
#define REG_MAX_PAYLOAD_LENGTH 0x23
#define REG_OSC 0x24
#define REG_HOP_PERIOD 0x24
#define REG_PREAMBLE_MSB_FSK 0x25
#define REG_FIFO_RX_BYTE_ADDR 0x25
#define REG_PREAMBLE_LSB_FSK 0x26
#define REG_MODEM_CONFIG3 0x26
#define REG_SYNC_CONFIG 0x27
#define REG_SYNC_VALUE1 0x28
#define REG_LORA_FEI_MSB 0x28
#define REG_SYNC_VALUE2 0x29
#define REG_LORA_FEI_MID 0x29
#define REG_SYNC_VALUE3 0x2A
#define REG_LORA_FEI_LSB 0x2A
#define REG_SYNC_VALUE4 0x2B
#define REG_SYNC_VALUE5 0x2C
#define REG_RSSI_WIDEBAND 0x2C
#define REG_SYNC_VALUE6 0x2D
#define REG_SYNC_VALUE7 0x2E
#define REG_SYNC_VALUE8 0x2F
#define REG_PACKET_CONFIG1 0x30
#define REG_PACKET_CONFIG2 0x31
#define REG_DETECT_OPTIMIZE 0x31
#define REG_PAYLOAD_LENGTH_FSK 0x32
#define REG_NODE_ADRS 0x33
#define REG_INVERT_IQ 0x33
#define REG_BROADCAST_ADRS 0x34
#define REG_FIFO_THRESH 0x35
#define REG_SEQ_CONFIG1 0x36
#define REG_SEQ_CONFIG2 0x37
#define REG_DETECTION_THRESHOLD 0x37
#define REG_TIMER_RESOL 0x38
#define REG_TIMER1_COEF 0x39
#define REG_SYNC_WORD 0x39
#define REG_TIMER2_COEF 0x3A
#define REG_IMAGE_CAL 0x3B
#define REG_TEMP 0x3C
#define REG_LOW_BAT 0x3D
#define REG_IRQ_FLAGS1 0x3E
#define REG_IRQ_FLAGS2 0x3F
#define REG_DIO_MAPPING1 0x40
#define REG_DIO_MAPPING2 0x41
#define REG_VERSION 0x42
#define REG_PLL_HOP 0x44
#define REG_TCXO 0x4B
#define REG_PA_DAC 0x4D
#define REG_FORMER_TEMP 0x5B
#define REG_BIT_RATE_FRAC 0x5D
#define REG_AGC_REF 0x61
#define REG_AGC_THRESH1 0x62
#define REG_AGC_THRESH2 0x63
#define REG_AGC_THRESH3 0x64
#define REG_PLL 0x70
//FSK MODES:
const uint8_t FSK_SLEEP_MODE = 0x00;
const uint8_t FSK_STANDBY_MODE = 0x01;
const uint8_t FSK_TX_MODE = 0x03;
const uint8_t FSK_RX_MODE = 0x05;
/******************************************************************************
* SX1278FSK Class
* Functions and variables for managing SX127x transceiver chips in FSK mode,
* mainly for receiving radiosonde transmissions
******************************************************************************/
class SX1278FSK
{
public:
// class constructor
SX1278FSK();
// Turn on SX1278 module (return 0 on sucess, 1 otherwise)
uint8_t ON();
// Turn off SX1278 module
void OFF();
// Read internal register
byte readRegister(byte address);
// Write internal register
void writeRegister(byte address, byte data);
// Clear IRQ flags
void clearIRQFlags();
// Activate FSK mode (return 0 on success, 1 otherwise)
uint8_t setFSK();
// Configures bitrate register (closest approximation to requested bitrate)
uint8_t setBitrate(float bps);
float getBitrate();
// Configures RX bandwidth (next largest supported bandwith if exact value not possible)
uint8_t setRxBandwidth(float bps);
float getRxBandwidth();
// Configures AFC bandwidth (next largest supported bandwith if exact value not possible)
uint8_t setAFCBandwidth(float bps);
float getAFCBandwidth();
// Configures RX frequency (closest approximation to requested frequency)
uint8_t setFrequency(float freq);
float getFrequency();
int getLNAGain();
uint8_t setLNAGain(int gain);
uint8_t getRxConf();
uint8_t setRxConf(uint8_t conf);
uint8_t setSyncConf(uint8_t conf, int len, const uint8_t *syncpattern);
uint8_t getSyncConf();
uint8_t setPreambleDetect(uint8_t conf);
uint8_t getPreambleDetect();
uint8_t setPacketConfig(uint8_t conf1, uint8_t conf2);
uint16_t getPacketConfig();
// Get configured preamble length (used for TX only?)
uint16_t getPreambleLength();
// Sets the preamble length.
uint8_t setPreambleLength(uint16_t l);
// Gets the payload length (expected length for receive)
int getPayloadLength();
uint8_t setPayloadLength(int len);
// Get current RSSI value
int16_t getRSSI();
// Get current FEI (frequency error indication) value
int32_t getFEI();
// Get current AFC value
int32_t getAFC();
// Get the maximum current supply by the module.
int getMaxCurrent();
// Set the maximum current supply by the module.
int8_t setMaxCurrent(uint8_t rate);
// Put the module in reception mode.
//return '0' on success, '1' otherwise
uint8_t receive();
// Receive a packet
uint8_t receivePacketTimeout(uint32_t wait, byte *data);
#if 0
//! It gets the internal temperature of the module.
/*!
It stores in global '_temp' variable the module temperature.
\return '0' on success, '1' otherwise
*/
uint8_t getTemp();
//! It prints the registers related to RX via USB
/*!
* \return void
*/
void showRxRegisters();
#endif
};
extern SX1278FSK sx1278;
#endif

347
libraries/SondeLib/DFM.cpp Executable file
View File

@ -0,0 +1,347 @@
/* DFM decoder functions */
#include "DFM.h"
#include "SX1278FSK.h"
#include "Sonde.h"
#define DFM_DEBUG 1
#if DFM_DEBUG
#define DFM_DBG(x) x
#else
#define DFM_DBG(x)
#endif
int DFM::setup(float frequency, int inv)
{
inverse = inv;
#if DFM_DEBUG
Serial.printf("Setup sx1278 for DFM sonde (inv=%d)\n",inv);
#endif
if(sx1278.ON()!=0) {
DFM_DBG(Serial.println("Setting SX1278 power on FAILED"));
return 1;
}
if(sx1278.setFSK()!=0) {
DFM_DBG(Serial.println("Setting FSM mode FAILED"));
return 1;
}
if(sx1278.setBitrate(2500)!=0) {
DFM_DBG(Serial.println("Setting bitrate 2500bit/s FAILED"));
return 1;
}
#if DFM_DEBUG
float br = sx1278.getBitrate();
Serial.print("Exact bitrate is ");
Serial.println(br);
#endif
if(sx1278.setAFCBandwidth(sonde.config.dfm.agcbw)!=0) {
DFM_DBG(Serial.printf("Setting AFC bandwidth %d Hz FAILED", sonde.config.dfm.agcbw));
return 1;
}
if(sx1278.setRxBandwidth(sonde.config.dfm.rxbw)!=0) {
DFM_DBG(Serial.printf("Setting RX bandwidth to %d Hz FAILED", sonde.config.dfm.rxbw));
return 1;
}
// Enable auto-AFC, auto-AGC, RX Trigger by preamble
if(sx1278.setRxConf(0x1E)!=0) {
DFM_DBG(Serial.println("Setting RX Config FAILED"));
return 1;
}
// Set autostart_RX to 01, preamble 0, SYNC detect==on, syncsize=3 (==4 byte
//char header[] = "0110.0101 0110.0110 1010.0101 1010.1010";
const char *SYNC=inverse?"\x9A\x99\x5A\x55":"\x65\x66\xA5\xAA";
if(sx1278.setSyncConf(0x53, 4, (const uint8_t *)SYNC)!=0) {
DFM_DBG(Serial.println("Setting SYNC Config FAILED"));
return 1;
}
if(sx1278.setPreambleDetect(0xA8)!=0) {
DFM_DBG(Serial.println("Setting PreambleDetect FAILED"));
return 1;
}
// Packet config 1: fixed len, mancecer, no crc, no address filter
// Packet config 2: packet mode, no home ctrl, no beackn, msb(packetlen)=0)
if(sx1278.setPacketConfig(0x28, 0x40)!=0) {
DFM_DBG(Serial.println("Setting Packet config FAILED"));
return 1;
}
Serial.print("DFM: setting RX frequency to ");
Serial.println(frequency);
int retval = sx1278.setFrequency(frequency);
sx1278.clearIRQFlags();
DFM_DBG(Serial.println("Setting SX1278 config for DFM finished\n"); Serial.println());
return retval;
}
#define bitpick(value,bitpos) (((value)>>(7-(bitpos)))&0x01)
// Input: str: packed data, MSB first
void DFM::deinterleave(uint8_t *str, int L, uint8_t *block) {
int i, j;
for (j = 0; j < B; j++) { // L = 7 (CFG), 13 (DAT1, DAT2)
for (i = 0; i < L; i++) {
block[B*i+j] = bitpick( str[(L*j+i)/8], (L*j+i)&7 )?0:1;
}
}
}
uint32_t DFM::bits2val(const uint8_t *bits, int len) {
uint32_t val = 0;
for (int j = 0; j < len; j++) {
val |= (bits[j] << (len-1-j));
}
return val;
}
// Error correction for hamming code
// returns 0: ok >0: 1 error was corrected -1: uncorrectable error
int DFM::check(uint8_t code[8]) {
int i, j;
uint32_t synval = 0;
uint8_t syndrom[4];
int ret=0;
for (i = 0; i < 4; i++) {
syndrom[i] = 0;
for (j = 0; j < 8; j++) {
syndrom[i] ^= H[i][j] & code[j];
}
}
synval = bits2val(syndrom, 4);
if (synval) {
ret = -1;
for (j = 0; j < 8; j++) { // 1-bit-error
if (synval == He[j]) { // reicht auf databits zu pruefen, d.h.
ret = j+1; // (systematischer Code) He[0..3]
break;
}
}
}
else ret = 0;
if (ret > 0) code[ret-1] ^= 0x1;
return ret;
}
// Extended (8,4) Hamming code
// Return number of corrected bits, -1 if uncorrectable error
int DFM::hamming(uint8_t *ham, int L, uint8_t *sym) {
int i, j;
int ret = 0; // DFM: length L = 7 or 13
for (i = 0; i < L; i++) { // L bytes (4bit data, 4bit parity)
if (use_ecc) {
int res = check(ham+8*i);
if(ret>=0 && res>=0) ret += res; else ret=-1;
}
// systematic Hamming code: copy bits 0..3
for (j = 0; j < 4; j++) {
sym[4*i+j] = ham[8*i+j];
}
}
return ret;
}
DFM::DFM() {
}
void DFM::printRaw(const char *label, int len, int ret, const uint8_t *data)
{
Serial.print(label); Serial.print("(");
Serial.print(ret);
Serial.print("):");
int i;
for(i=0; i<len/2; i++) {
char str[10];
snprintf(str, 10, "%02X", data[i]);
Serial.print(str);
}
Serial.print(data[i]&0x0F, HEX);
Serial.print(" ");
}
void DFM::decodeCFG(uint8_t *cfg)
{
static int lowid, highid, idgood=0, type=0;
if((cfg[0]>>4)==0x06 && type==0) { // DFM-6 ID
lowid = ((cfg[0]&0x0F)<<20) | (cfg[1]<<12) | (cfg[2]<<4) | (cfg[3]&0x0f);
Serial.print("DFM-06 ID: "); Serial.print(lowid, HEX);
snprintf(sonde.si()->id, 10, "%x", lowid);
sonde.si()->validID = true;
}
if((cfg[0]>>4)==0x0A) { // DMF-9 ID
type=9;
if(cfg[3]==1) {
lowid = (cfg[1]<<8) | cfg[2];
idgood |= 1;
} else {
highid = (cfg[1]<<8) | cfg[2];
idgood |= 2;
}
if(idgood==3) {
uint32_t dfmid = (highid<<16) | lowid;
Serial.print("DFM-09 ID: "); Serial.print(dfmid);
snprintf(sonde.si()->ser, 10, "%d", dfmid);
// dxlAPRS sonde number (DF6 (why??) and 5 last digits of serial number as hex number
snprintf(sonde.si()->id, 9, "DF6%05X", dfmid&0xfffff);
sonde.si()->validID = true;
}
}
}
static int bitCount(int x) {
int m4 = 0x1 | (0x1<<8) | (0x1<<16) | (0x1<<24);
int m1 = 0xFF;
int s4 = (x&m4) + ((x>>1)&m4) + ((x>>2)&m4) + ((x>>3)&m4) + ((x>>4)&m4) + ((x>>5)&m4) + ((x>>6)&m4) + ((x>>7)&m4);
int s1 = (s4&m1) + ((s4>>8)&m1) + ((s4>>16)&m1) + ((s4>>24)&m1);
return s1;
}
static uint16_t MON[]={0,0,31,59,90,120,151,181,212,243,273,304,334};
void DFM::decodeDAT(uint8_t *dat)
{
Serial.print(" DAT["); Serial.print(dat[6]); Serial.print("]: ");
switch(dat[6]) {
case 0:
Serial.print("Packet counter: "); Serial.print(dat[3]);
sonde.si()->frame = dat[3];
break;
case 1:
{
int val = (((uint16_t)dat[4])<<8) + (uint16_t)dat[5];
Serial.print("UTC-msec: "); Serial.print(val);
sonde.si()->sec = val/1000;
uint32_t tmp = ((uint32_t)dat[0]<<24) + ((uint32_t)dat[1]<<16) + ((uint32_t)dat[2]<<8) + ((uint32_t)dat[3]);
sonde.si()->sats = bitCount(tmp); // maybe!?!?!?
}
break;
case 2:
{
float lat, vh;
lat = ((uint32_t)dat[0]<<24) + ((uint32_t)dat[1]<<16) + ((uint32_t)dat[2]<<8) + ((uint32_t)dat[3]);
vh = ((uint16_t)dat[4]<<8) + dat[5];
Serial.print("GPS-lat: "); Serial.print(lat*0.0000001);
Serial.print(", hor-V: "); Serial.print(vh*0.01);
sonde.si()->lat = lat*0.0000001;
sonde.si()->hs = vh*0.01;
sonde.si()->validPos |= 0x11;
}
break;
case 3:
{
float lon, dir;
lon = ((uint32_t)dat[0]<<24) + ((uint32_t)dat[1]<<16) + ((uint32_t)dat[2]<<8) + (uint32_t)dat[3];
dir = ((uint16_t)dat[4]<<8) + dat[5];
Serial.print("GPS-lon: "); Serial.print(lon*0.0000001);
Serial.print(", dir: "); Serial.print(dir*0.01);
sonde.si()->lon = lon*0.0000001;
sonde.si()->dir = dir*0.01;
sonde.si()->validPos |= 0x42;
}
break;
case 4:
{
float alt, vv;
alt = ((uint32_t)dat[0]<<24) + ((uint32_t)dat[1]<<16) + ((uint32_t)dat[2]<<8) + dat[3];
vv = (int16_t)( ((int16_t)dat[4]<<8) | dat[5] );
Serial.print("GPS-height: "); Serial.print(alt*0.01);
Serial.print(", vv: "); Serial.print(vv*0.01);
sonde.si()->alt = alt*0.01;
sonde.si()->vs = vv*0.01;
sonde.si()->validPos |= 0x0C;
}
break;
case 8:
{
int y = (dat[0]<<4) + (dat[1]>>4);
int m = dat[1]&0x0F;
int d = dat[2]>>3;
int h = ((dat[2]&0x07)<<2) + (dat[3]>>6);
int mi = (dat[3]&0x3F);
char buf[100];
snprintf(buf, 100, "%04d-%02d-%02d %02d:%02dz", y, m, d, h, mi);
Serial.print("Date: "); Serial.print(buf);
// convert to unix time
int tt = (y-1970)*365 + (y-1969)/4; // days since 1970
if(m<=12) { tt += MON[m]; if((y%4)==0 && m>2) tt++; }
tt = (tt+d-1)*(60*60*24) + h*3600 + mi*60;
sonde.si()->time = tt;
}
break;
default:
Serial.print("(?)");
break;
}
}
void DFM::bitsToBytes(uint8_t *bits, uint8_t *bytes, int len)
{
int i;
for(i=0; i<len*4; i++) {
//Serial.print(bits[i]?"1":"0");
bytes[i/8] = (bytes[i/8]<<1) | (bits[i]?1:0);
}
bytes[(i-1)/8] &= 0x0F;
}
int DFM::receive() {
byte data[1000]; // pending data from previous mode may write more than 33 bytes. TODO.
for(int i=0; i<2; i++) {
sx1278.setPayloadLength(33); // Expect 33 bytes (7+13+13 bytes)
sx1278.writeRegister(REG_OP_MODE, FSK_RX_MODE);
int e = sx1278.receivePacketTimeout(1000, data);
if(e) { return RX_TIMEOUT; } //if timeout... return 1
Serial.printf("inverse is %d\b", inverse);
if(!inverse) { for(int i=0; i<33; i++) { data[i]^=0xFF; } }
deinterleave(data, 7, hamming_conf);
deinterleave(data+7, 13, hamming_dat1);
deinterleave(data+20, 13, hamming_dat2);
int ret0 = hamming(hamming_conf, 7, block_conf);
int ret1 = hamming(hamming_dat1, 13, block_dat1);
int ret2 = hamming(hamming_dat2, 13, block_dat2);
byte byte_conf[4], byte_dat1[7], byte_dat2[7];
bitsToBytes(block_conf, byte_conf, 7);
bitsToBytes(block_dat1, byte_dat1, 13);
bitsToBytes(block_dat2, byte_dat2, 13);
printRaw("CFG", 7, ret0, byte_conf);
printRaw("DAT", 13, ret1, byte_dat1);
printRaw("DAT", 13, ret2, byte_dat2);
decodeCFG(byte_conf);
decodeDAT(byte_dat1);
decodeDAT(byte_dat2);
}
return RX_OK;
}
// moved to a single function in Sonde(). This function can be used for additional
// processing here, that takes too long for doing in the RX task loop
int DFM::waitRXcomplete() {
#if 0
int res=0;
uint32_t t0 = millis();
while( rxtask.receiveResult < 0 && millis()-t0 < 2000) { delay(50); }
if( rxtask.receiveResult<0 || rxtask.receiveResult==RX_TIMEOUT) {
res = RX_TIMEOUT;
} else if ( rxtask.receiveResult ==0) {
res = RX_OK;
} else {
res = RX_ERROR;
}
rxtask.receiveResult = -1;
Serial.printf("waitRXcomplete returning %d\n", res);
return res;
#endif
return 0;
}
DFM dfm = DFM();

67
libraries/SondeLib/DFM.h Executable file
View File

@ -0,0 +1,67 @@
/*
* DFM.h
* Functions for decoding DFM sondes with SX127x chips
* Copyright (C) 2019 Hansi Reiser, dl9rdz
*
* SPDX-License-Identifier: GPL-2.0+
*/
#ifndef DFM_h
#define DFM_h
#include <stdlib.h>
#include <stdint.h>
#include <Arduino.h>
#ifndef inttypes_h
#include <inttypes.h>
#endif
#define DFM_NORMAL 0
#define DFM_INVERSE 1
/* Main class */
class DFM
{
private:
int inverse=0;
void deinterleave(uint8_t *str, int L, uint8_t *block);
uint32_t bits2val(const uint8_t *bits, int len);
int check(uint8_t code[8]);
int hamming(uint8_t *ham, int L, uint8_t *sym);
void printRaw(const char *prefix, int len, int ret, const uint8_t* data);
void decodeCFG(uint8_t *cfg);
void decodeDAT(uint8_t *dat);
void bitsToBytes(uint8_t *bits, uint8_t *bytes, int len);
#define B 8
#define S 4
uint8_t hamming_conf[ 7*B]; // 7*8=56
uint8_t hamming_dat1[13*B]; // 13*8=104
uint8_t hamming_dat2[13*B];
uint8_t block_conf[ 7*S]; // 7*4=28
uint8_t block_dat1[13*S]; // 13*4=52
uint8_t block_dat2[13*S];
uint8_t H[4][8] = // extended Hamming(8,4) particy check matrix
{{ 0, 1, 1, 1, 1, 0, 0, 0},
{ 1, 0, 1, 1, 0, 1, 0, 0},
{ 1, 1, 0, 1, 0, 0, 1, 0},
{ 1, 1, 1, 0, 0, 0, 0, 1}};
uint8_t He[8] = { 0x7, 0xB, 0xD, 0xE, 0x8, 0x4, 0x2, 0x1}; // Spalten von H:
// 1-bit-error-Syndrome
public:
DFM();
// main decoder API
int setup(float frequency, int inverse);
int receive();
int waitRXcomplete();
int use_ecc = 1;
};
extern DFM dfm;
#endif

367
libraries/SondeLib/DefaultFonts.c Executable file
View File

@ -0,0 +1,367 @@
// SPDX-License-Identifier: GPL-3.0
// original source: https://github.com/Nkawu/TFT_22_ILI9225
#ifdef __AVR__
#include <avr/pgmspace.h>
#elif defined(ESP8266) || defined(ESP32)
#include <pgmspace.h>
#endif
#if defined(ARDUINO_ARCH_SAM) || defined(ARDUINO_ARCH_SAMD) || defined(ARDUINO_ARCH_STM32) || defined(ARDUINO_ARCH_STM32F1) || defined(STM32F1) || defined(ESP32)
#define PROGMEM
#define fontdatatype const char
#else
#define fontdatatype const uint8_t
#endif
//Font Generated by MikroElektronika GLCD Font Creator 1.2.0.0
//MikroElektronika 2011
//http://www.mikroe.com
//GLCD FontName : Terminal6x8
//GLCD FontSize : 6 x 8
fontdatatype Terminal6x8[] PROGMEM = {
0x06, 0x08, 0x20, 0x60,
0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char
0x05, 0x00, 0x00, 0x06, 0x5F, 0x06, 0x00, // Code for char !
0x06, 0x00, 0x07, 0x03, 0x00, 0x07, 0x03, // Code for char "
0x06, 0x00, 0x24, 0x7E, 0x24, 0x7E, 0x24, // Code for char #
0x05, 0x00, 0x24, 0x2B, 0x6A, 0x12, 0x00, // Code for char $
0x06, 0x00, 0x63, 0x13, 0x08, 0x64, 0x63, // Code for char %
0x06, 0x00, 0x36, 0x49, 0x56, 0x20, 0x50, // Code for char &
0x04, 0x00, 0x00, 0x07, 0x03, 0x00, 0x00, // Code for char '
0x04, 0x00, 0x00, 0x3E, 0x41, 0x00, 0x00, // Code for char (
0x04, 0x00, 0x00, 0x41, 0x3E, 0x00, 0x00, // Code for char )
0x06, 0x00, 0x08, 0x3E, 0x1C, 0x3E, 0x08, // Code for char *
0x06, 0x00, 0x08, 0x08, 0x3E, 0x08, 0x08, // Code for char +
0x04, 0x00, 0x00, 0xE0, 0x60, 0x00, 0x00, // Code for char ,
0x06, 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, // Code for char -
0x04, 0x00, 0x00, 0x60, 0x60, 0x00, 0x00, // Code for char .
0x06, 0x00, 0x20, 0x10, 0x08, 0x04, 0x02, // Code for char /
0x06, 0x00, 0x3E, 0x51, 0x49, 0x45, 0x3E, // Code for char 0
0x05, 0x00, 0x00, 0x42, 0x7F, 0x40, 0x00, // Code for char 1
0x06, 0x00, 0x62, 0x51, 0x49, 0x49, 0x46, // Code for char 2
0x06, 0x00, 0x22, 0x49, 0x49, 0x49, 0x36, // Code for char 3
0x06, 0x00, 0x18, 0x14, 0x12, 0x7F, 0x10, // Code for char 4
0x06, 0x00, 0x2F, 0x49, 0x49, 0x49, 0x31, // Code for char 5
0x06, 0x00, 0x3C, 0x4A, 0x49, 0x49, 0x30, // Code for char 6
0x06, 0x00, 0x01, 0x71, 0x09, 0x05, 0x03, // Code for char 7
0x06, 0x00, 0x36, 0x49, 0x49, 0x49, 0x36, // Code for char 8
0x06, 0x00, 0x06, 0x49, 0x49, 0x29, 0x1E, // Code for char 9
0x04, 0x00, 0x00, 0x6C, 0x6C, 0x00, 0x00, // Code for char :
0x04, 0x00, 0x00, 0xEC, 0x6C, 0x00, 0x00, // Code for char ;
0x05, 0x00, 0x08, 0x14, 0x22, 0x41, 0x00, // Code for char <
0x06, 0x00, 0x24, 0x24, 0x24, 0x24, 0x24, // Code for char =
0x06, 0x00, 0x00, 0x41, 0x22, 0x14, 0x08, // Code for char >
0x06, 0x00, 0x02, 0x01, 0x59, 0x09, 0x06, // Code for char ?
0x06, 0x00, 0x3E, 0x41, 0x5D, 0x55, 0x1E, // Code for char @
0x06, 0x00, 0x7E, 0x11, 0x11, 0x11, 0x7E, // Code for char A
0x06, 0x00, 0x7F, 0x49, 0x49, 0x49, 0x36, // Code for char B
0x06, 0x00, 0x3E, 0x41, 0x41, 0x41, 0x22, // Code for char C
0x06, 0x00, 0x7F, 0x41, 0x41, 0x41, 0x3E, // Code for char D
0x06, 0x00, 0x7F, 0x49, 0x49, 0x49, 0x41, // Code for char E
0x06, 0x00, 0x7F, 0x09, 0x09, 0x09, 0x01, // Code for char F
0x06, 0x00, 0x3E, 0x41, 0x49, 0x49, 0x7A, // Code for char G
0x06, 0x00, 0x7F, 0x08, 0x08, 0x08, 0x7F, // Code for char H
0x05, 0x00, 0x00, 0x41, 0x7F, 0x41, 0x00, // Code for char I
0x06, 0x00, 0x30, 0x40, 0x40, 0x40, 0x3F, // Code for char J
0x06, 0x00, 0x7F, 0x08, 0x14, 0x22, 0x41, // Code for char K
0x06, 0x00, 0x7F, 0x40, 0x40, 0x40, 0x40, // Code for char L
0x06, 0x00, 0x7F, 0x02, 0x04, 0x02, 0x7F, // Code for char M
0x06, 0x00, 0x7F, 0x02, 0x04, 0x08, 0x7F, // Code for char N
0x06, 0x00, 0x3E, 0x41, 0x41, 0x41, 0x3E, // Code for char O
0x06, 0x00, 0x7F, 0x09, 0x09, 0x09, 0x06, // Code for char P
0x06, 0x00, 0x3E, 0x41, 0x51, 0x21, 0x5E, // Code for char Q
0x06, 0x00, 0x7F, 0x09, 0x09, 0x19, 0x66, // Code for char R
0x06, 0x00, 0x26, 0x49, 0x49, 0x49, 0x32, // Code for char S
0x06, 0x00, 0x01, 0x01, 0x7F, 0x01, 0x01, // Code for char T
0x06, 0x00, 0x3F, 0x40, 0x40, 0x40, 0x3F, // Code for char U
0x06, 0x00, 0x1F, 0x20, 0x40, 0x20, 0x1F, // Code for char V
0x06, 0x00, 0x3F, 0x40, 0x3C, 0x40, 0x3F, // Code for char W
0x06, 0x00, 0x63, 0x14, 0x08, 0x14, 0x63, // Code for char X
0x06, 0x00, 0x07, 0x08, 0x70, 0x08, 0x07, // Code for char Y
0x05, 0x00, 0x71, 0x49, 0x45, 0x43, 0x00, // Code for char Z
0x05, 0x00, 0x00, 0x7F, 0x41, 0x41, 0x00, // Code for char [
0x06, 0x00, 0x02, 0x04, 0x08, 0x10, 0x20, // Code for char BackSlash
0x05, 0x00, 0x00, 0x41, 0x41, 0x7F, 0x00, // Code for char ]
0x06, 0x00, 0x04, 0x02, 0x01, 0x02, 0x04, // Code for char ^
0x06, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, // Code for char _
0x04, 0x00, 0x00, 0x03, 0x07, 0x00, 0x00, // Code for char `
0x06, 0x00, 0x20, 0x54, 0x54, 0x54, 0x78, // Code for char a
0x06, 0x00, 0x7F, 0x44, 0x44, 0x44, 0x38, // Code for char b
0x06, 0x00, 0x38, 0x44, 0x44, 0x44, 0x28, // Code for char c
0x06, 0x00, 0x38, 0x44, 0x44, 0x44, 0x7F, // Code for char d
0x06, 0x00, 0x38, 0x54, 0x54, 0x54, 0x08, // Code for char e
0x05, 0x00, 0x08, 0x7E, 0x09, 0x09, 0x00, // Code for char f
0x06, 0x00, 0x18, 0xA4, 0xA4, 0xA4, 0x7C, // Code for char g
0x05, 0x00, 0x7F, 0x04, 0x04, 0x78, 0x00, // Code for char h
0x05, 0x00, 0x00, 0x00, 0x7D, 0x40, 0x00, // Code for char i
0x05, 0x00, 0x40, 0x80, 0x84, 0x7D, 0x00, // Code for char j
0x05, 0x00, 0x7F, 0x10, 0x28, 0x44, 0x00, // Code for char k
0x05, 0x00, 0x00, 0x00, 0x7F, 0x40, 0x00, // Code for char l
0x06, 0x00, 0x7C, 0x04, 0x18, 0x04, 0x78, // Code for char m
0x05, 0x00, 0x7C, 0x04, 0x04, 0x78, 0x00, // Code for char n
0x06, 0x00, 0x38, 0x44, 0x44, 0x44, 0x38, // Code for char o
0x06, 0x00, 0xFC, 0x44, 0x44, 0x44, 0x38, // Code for char p
0x06, 0x00, 0x38, 0x44, 0x44, 0x44, 0xFC, // Code for char q
0x06, 0x00, 0x44, 0x78, 0x44, 0x04, 0x08, // Code for char r
0x06, 0x00, 0x08, 0x54, 0x54, 0x54, 0x20, // Code for char s
0x05, 0x00, 0x04, 0x3E, 0x44, 0x24, 0x00, // Code for char t
0x05, 0x00, 0x3C, 0x40, 0x20, 0x7C, 0x00, // Code for char u
0x06, 0x00, 0x1C, 0x20, 0x40, 0x20, 0x1C, // Code for char v
0x06, 0x00, 0x3C, 0x60, 0x30, 0x60, 0x3C, // Code for char w
0x05, 0x00, 0x6C, 0x10, 0x10, 0x6C, 0x00, // Code for char x
0x05, 0x00, 0x9C, 0xA0, 0x60, 0x3C, 0x00, // Code for char y
0x05, 0x00, 0x64, 0x54, 0x54, 0x4C, 0x00, // Code for char z
0x05, 0x00, 0x08, 0x3E, 0x41, 0x41, 0x00, // Code for char {
0x04, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, // Code for char |
0x06, 0x00, 0x00, 0x41, 0x41, 0x3E, 0x08, // Code for char }
0x05, 0x00, 0x02, 0x01, 0x02, 0x01, 0x00, // Code for char ~
0x06, 0x00, 0x3C, 0x26, 0x23, 0x26, 0x3C // Code for char 
};
//Font Generated by MikroElektronika GLCD Font Creator 1.2.0.0
//MikroElektronika 2011
//http://www.mikroe.com
//GLCD FontName : Terminal11x16
//GLCD FontSize : 11 x 16
fontdatatype Terminal11x16[] PROGMEM = {
0x0B, 0x10, 0x20, 0x60,
0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char
0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0x00, 0xFF, 0x33, 0xFF, 0x33, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char !
0x08, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char "
0x0B, 0x00, 0x02, 0x10, 0x1E, 0x90, 0x1F, 0xF0, 0x03, 0x7E, 0x02, 0x1E, 0x1E, 0x90, 0x1F, 0xF0, 0x03, 0x7E, 0x02, 0x1E, 0x00, 0x10, 0x00, // Code for char #
0x09, 0x00, 0x00, 0x78, 0x04, 0xFC, 0x0C, 0xCC, 0x0C, 0xFF, 0x3F, 0xFF, 0x3F, 0xCC, 0x0C, 0xCC, 0x0F, 0x88, 0x07, 0x00, 0x00, 0x00, 0x00, // Code for char $
0x0B, 0x00, 0x30, 0x38, 0x38, 0x38, 0x1C, 0x38, 0x0E, 0x00, 0x07, 0x80, 0x03, 0xC0, 0x01, 0xE0, 0x38, 0x70, 0x38, 0x38, 0x38, 0x1C, 0x00, // Code for char %
0x0A, 0x00, 0x00, 0x00, 0x1F, 0xB8, 0x3F, 0xFC, 0x31, 0xC6, 0x21, 0xE2, 0x37, 0x3E, 0x1E, 0x1C, 0x1C, 0x00, 0x36, 0x00, 0x22, 0x00, 0x00, // Code for char &
0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x27, 0x00, 0x3F, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char '
0x08, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x03, 0xFC, 0x0F, 0xFE, 0x1F, 0x07, 0x38, 0x01, 0x20, 0x01, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char (
0x08, 0x00, 0x00, 0x00, 0x00, 0x01, 0x20, 0x01, 0x20, 0x07, 0x38, 0xFE, 0x1F, 0xFC, 0x0F, 0xF0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char )
0x09, 0x00, 0x00, 0x98, 0x0C, 0xB8, 0x0E, 0xE0, 0x03, 0xF8, 0x0F, 0xF8, 0x0F, 0xE0, 0x03, 0xB8, 0x0E, 0x98, 0x0C, 0x00, 0x00, 0x00, 0x00, // Code for char *
0x09, 0x00, 0x00, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0xF0, 0x0F, 0xF0, 0x0F, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, // Code for char +
0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB8, 0x00, 0xF8, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ,
0x09, 0x00, 0x00, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, // Code for char -
0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char .
0x0B, 0x00, 0x18, 0x00, 0x1C, 0x00, 0x0E, 0x00, 0x07, 0x80, 0x03, 0xC0, 0x01, 0xE0, 0x00, 0x70, 0x00, 0x38, 0x00, 0x1C, 0x00, 0x0E, 0x00, // Code for char /
0x0B, 0xF8, 0x07, 0xFE, 0x1F, 0x06, 0x1E, 0x03, 0x33, 0x83, 0x31, 0xC3, 0x30, 0x63, 0x30, 0x33, 0x30, 0x1E, 0x18, 0xFE, 0x1F, 0xF8, 0x07, // Code for char 0
0x0A, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x30, 0x0C, 0x30, 0x0E, 0x30, 0xFF, 0x3F, 0xFF, 0x3F, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x00, // Code for char 1
0x0B, 0x1C, 0x30, 0x1E, 0x38, 0x07, 0x3C, 0x03, 0x3E, 0x03, 0x37, 0x83, 0x33, 0xC3, 0x31, 0xE3, 0x30, 0x77, 0x30, 0x3E, 0x30, 0x1C, 0x30, // Code for char 2
0x0B, 0x0C, 0x0C, 0x0E, 0x1C, 0x07, 0x38, 0xC3, 0x30, 0xC3, 0x30, 0xC3, 0x30, 0xC3, 0x30, 0xC3, 0x30, 0xE7, 0x39, 0x7E, 0x1F, 0x3C, 0x0E, // Code for char 3
0x0B, 0xC0, 0x03, 0xE0, 0x03, 0x70, 0x03, 0x38, 0x03, 0x1C, 0x03, 0x0E, 0x03, 0x07, 0x03, 0xFF, 0x3F, 0xFF, 0x3F, 0x00, 0x03, 0x00, 0x03, // Code for char 4
0x0B, 0x3F, 0x0C, 0x7F, 0x1C, 0x63, 0x38, 0x63, 0x30, 0x63, 0x30, 0x63, 0x30, 0x63, 0x30, 0x63, 0x30, 0xE3, 0x38, 0xC3, 0x1F, 0x83, 0x0F, // Code for char 5
0x0B, 0xC0, 0x0F, 0xF0, 0x1F, 0xF8, 0x39, 0xDC, 0x30, 0xCE, 0x30, 0xC7, 0x30, 0xC3, 0x30, 0xC3, 0x30, 0xC3, 0x39, 0x80, 0x1F, 0x00, 0x0F, // Code for char 6
0x0B, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x30, 0x03, 0x3C, 0x03, 0x0F, 0xC3, 0x03, 0xF3, 0x00, 0x3F, 0x00, 0x0F, 0x00, 0x03, 0x00, // Code for char 7
0x0B, 0x00, 0x0F, 0xBC, 0x1F, 0xFE, 0x39, 0xE7, 0x30, 0xC3, 0x30, 0xC3, 0x30, 0xC3, 0x30, 0xE7, 0x30, 0xFE, 0x39, 0xBC, 0x1F, 0x00, 0x0F, // Code for char 8
0x0B, 0x3C, 0x00, 0x7E, 0x00, 0xE7, 0x30, 0xC3, 0x30, 0xC3, 0x30, 0xC3, 0x38, 0xC3, 0x1C, 0xC3, 0x0E, 0xE7, 0x07, 0xFE, 0x03, 0xFC, 0x00, // Code for char 9
0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x1C, 0x70, 0x1C, 0x70, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char :
0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x9C, 0x70, 0xFC, 0x70, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ;
0x09, 0x00, 0x00, 0xC0, 0x00, 0xE0, 0x01, 0xF0, 0x03, 0x38, 0x07, 0x1C, 0x0E, 0x0E, 0x1C, 0x07, 0x38, 0x03, 0x30, 0x00, 0x00, 0x00, 0x00, // Code for char <
0x0A, 0x00, 0x00, 0x60, 0x06, 0x60, 0x06, 0x60, 0x06, 0x60, 0x06, 0x60, 0x06, 0x60, 0x06, 0x60, 0x06, 0x60, 0x06, 0x60, 0x06, 0x00, 0x00, // Code for char =
0x09, 0x00, 0x00, 0x03, 0x30, 0x07, 0x38, 0x0E, 0x1C, 0x1C, 0x0E, 0x38, 0x07, 0xF0, 0x03, 0xE0, 0x01, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char >
0x0A, 0x1C, 0x00, 0x1E, 0x00, 0x07, 0x00, 0x03, 0x00, 0x83, 0x37, 0xC3, 0x37, 0xE3, 0x00, 0x77, 0x00, 0x3E, 0x00, 0x1C, 0x00, 0x00, 0x00, // Code for char ?
0x0B, 0xF8, 0x0F, 0xFE, 0x1F, 0x07, 0x18, 0xF3, 0x33, 0xFB, 0x37, 0x1B, 0x36, 0xFB, 0x37, 0xFB, 0x37, 0x07, 0x36, 0xFE, 0x03, 0xF8, 0x01, // Code for char @
0x0A, 0x00, 0x38, 0x00, 0x3F, 0xE0, 0x07, 0xFC, 0x06, 0x1F, 0x06, 0x1F, 0x06, 0xFC, 0x06, 0xE0, 0x07, 0x00, 0x3F, 0x00, 0x38, 0x00, 0x00, // Code for char A
0x0A, 0xFF, 0x3F, 0xFF, 0x3F, 0xC3, 0x30, 0xC3, 0x30, 0xC3, 0x30, 0xC3, 0x30, 0xE7, 0x30, 0xFE, 0x39, 0xBC, 0x1F, 0x00, 0x0F, 0x00, 0x00, // Code for char B
0x0A, 0xF0, 0x03, 0xFC, 0x0F, 0x0E, 0x1C, 0x07, 0x38, 0x03, 0x30, 0x03, 0x30, 0x03, 0x30, 0x07, 0x38, 0x0E, 0x1C, 0x0C, 0x0C, 0x00, 0x00, // Code for char C
0x0A, 0xFF, 0x3F, 0xFF, 0x3F, 0x03, 0x30, 0x03, 0x30, 0x03, 0x30, 0x03, 0x30, 0x07, 0x38, 0x0E, 0x1C, 0xFC, 0x0F, 0xF0, 0x03, 0x00, 0x00, // Code for char D
0x0A, 0xFF, 0x3F, 0xFF, 0x3F, 0xC3, 0x30, 0xC3, 0x30, 0xC3, 0x30, 0xC3, 0x30, 0xC3, 0x30, 0xC3, 0x30, 0x03, 0x30, 0x03, 0x30, 0x00, 0x00, // Code for char E
0x0A, 0xFF, 0x3F, 0xFF, 0x3F, 0xC3, 0x00, 0xC3, 0x00, 0xC3, 0x00, 0xC3, 0x00, 0xC3, 0x00, 0xC3, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, // Code for char F
0x0A, 0xF0, 0x03, 0xFC, 0x0F, 0x0E, 0x1C, 0x07, 0x38, 0x03, 0x30, 0xC3, 0x30, 0xC3, 0x30, 0xC3, 0x30, 0xC7, 0x3F, 0xC6, 0x3F, 0x00, 0x00, // Code for char G
0x0A, 0xFF, 0x3F, 0xFF, 0x3F, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xFF, 0x3F, 0xFF, 0x3F, 0x00, 0x00, // Code for char H
0x08, 0x00, 0x00, 0x00, 0x00, 0x03, 0x30, 0x03, 0x30, 0xFF, 0x3F, 0xFF, 0x3F, 0x03, 0x30, 0x03, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char I
0x0A, 0x00, 0x0E, 0x00, 0x1E, 0x00, 0x38, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x38, 0xFF, 0x1F, 0xFF, 0x07, 0x00, 0x00, // Code for char J
0x0A, 0xFF, 0x3F, 0xFF, 0x3F, 0xC0, 0x00, 0xE0, 0x01, 0xF0, 0x03, 0x38, 0x07, 0x1C, 0x0E, 0x0E, 0x1C, 0x07, 0x38, 0x03, 0x30, 0x00, 0x00, // Code for char K
0x0A, 0xFF, 0x3F, 0xFF, 0x3F, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x00, // Code for char L
0x0A, 0xFF, 0x3F, 0xFF, 0x3F, 0x1E, 0x00, 0x78, 0x00, 0xE0, 0x01, 0xE0, 0x01, 0x78, 0x00, 0x1E, 0x00, 0xFF, 0x3F, 0xFF, 0x3F, 0x00, 0x00, // Code for char M
0x0A, 0xFF, 0x3F, 0xFF, 0x3F, 0x0E, 0x00, 0x38, 0x00, 0xF0, 0x00, 0xC0, 0x03, 0x00, 0x07, 0x00, 0x1C, 0xFF, 0x3F, 0xFF, 0x3F, 0x00, 0x00, // Code for char N
0x0A, 0xF0, 0x03, 0xFC, 0x0F, 0x0E, 0x1C, 0x07, 0x38, 0x03, 0x30, 0x03, 0x30, 0x07, 0x38, 0x0E, 0x1C, 0xFC, 0x0F, 0xF0, 0x03, 0x00, 0x00, // Code for char O
0x0A, 0xFF, 0x3F, 0xFF, 0x3F, 0x83, 0x01, 0x83, 0x01, 0x83, 0x01, 0x83, 0x01, 0x83, 0x01, 0xC7, 0x01, 0xFE, 0x00, 0x7C, 0x00, 0x00, 0x00, // Code for char P
0x0A, 0xF0, 0x03, 0xFC, 0x0F, 0x0E, 0x1C, 0x07, 0x38, 0x03, 0x30, 0x03, 0x36, 0x07, 0x3E, 0x0E, 0x1C, 0xFC, 0x3F, 0xF0, 0x33, 0x00, 0x00, // Code for char Q
0x0A, 0xFF, 0x3F, 0xFF, 0x3F, 0x83, 0x01, 0x83, 0x01, 0x83, 0x03, 0x83, 0x07, 0x83, 0x0F, 0xC7, 0x1D, 0xFE, 0x38, 0x7C, 0x30, 0x00, 0x00, // Code for char R
0x0A, 0x3C, 0x0C, 0x7E, 0x1C, 0xE7, 0x38, 0xC3, 0x30, 0xC3, 0x30, 0xC3, 0x30, 0xC3, 0x30, 0xC7, 0x39, 0x8E, 0x1F, 0x0C, 0x0F, 0x00, 0x00, // Code for char S
0x09, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0xFF, 0x3F, 0xFF, 0x3F, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char T
0x0A, 0xFF, 0x07, 0xFF, 0x1F, 0x00, 0x38, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x38, 0xFF, 0x1F, 0xFF, 0x07, 0x00, 0x00, // Code for char U
0x0A, 0x07, 0x00, 0x3F, 0x00, 0xF8, 0x01, 0xC0, 0x0F, 0x00, 0x3E, 0x00, 0x3E, 0xC0, 0x0F, 0xF8, 0x01, 0x3F, 0x00, 0x07, 0x00, 0x00, 0x00, // Code for char V
0x0A, 0xFF, 0x3F, 0xFF, 0x3F, 0x00, 0x1C, 0x00, 0x06, 0x80, 0x03, 0x80, 0x03, 0x00, 0x06, 0x00, 0x1C, 0xFF, 0x3F, 0xFF, 0x3F, 0x00, 0x00, // Code for char W
0x0A, 0x03, 0x30, 0x0F, 0x3C, 0x1C, 0x0E, 0x30, 0x03, 0xE0, 0x01, 0xE0, 0x01, 0x30, 0x03, 0x1C, 0x0E, 0x0F, 0x3C, 0x03, 0x30, 0x00, 0x00, // Code for char X
0x0A, 0x03, 0x00, 0x0F, 0x00, 0x3C, 0x00, 0xF0, 0x00, 0xC0, 0x3F, 0xC0, 0x3F, 0xF0, 0x00, 0x3C, 0x00, 0x0F, 0x00, 0x03, 0x00, 0x00, 0x00, // Code for char Y
0x0A, 0x03, 0x30, 0x03, 0x3C, 0x03, 0x3E, 0x03, 0x33, 0xC3, 0x31, 0xE3, 0x30, 0x33, 0x30, 0x1F, 0x30, 0x0F, 0x30, 0x03, 0x30, 0x00, 0x00, // Code for char Z
0x08, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x3F, 0xFF, 0x3F, 0x03, 0x30, 0x03, 0x30, 0x03, 0x30, 0x03, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char [
0x0B, 0x0E, 0x00, 0x1C, 0x00, 0x38, 0x00, 0x70, 0x00, 0xE0, 0x00, 0xC0, 0x01, 0x80, 0x03, 0x00, 0x07, 0x00, 0x0E, 0x00, 0x1C, 0x00, 0x18, // Code for char BackSlash
0x08, 0x00, 0x00, 0x00, 0x00, 0x03, 0x30, 0x03, 0x30, 0x03, 0x30, 0x03, 0x30, 0xFF, 0x3F, 0xFF, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ]
0x0B, 0x60, 0x00, 0x70, 0x00, 0x38, 0x00, 0x1C, 0x00, 0x0E, 0x00, 0x07, 0x00, 0x0E, 0x00, 0x1C, 0x00, 0x38, 0x00, 0x70, 0x00, 0x60, 0x00, // Code for char ^
0x0B, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, // Code for char _
0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x7E, 0x00, 0x4E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char `
0x0A, 0x00, 0x1C, 0x40, 0x3E, 0x60, 0x33, 0x60, 0x33, 0x60, 0x33, 0x60, 0x33, 0x60, 0x33, 0x60, 0x33, 0xE0, 0x3F, 0xC0, 0x3F, 0x00, 0x00, // Code for char a
0x0A, 0xFF, 0x3F, 0xFF, 0x3F, 0xC0, 0x30, 0x60, 0x30, 0x60, 0x30, 0x60, 0x30, 0x60, 0x30, 0xE0, 0x38, 0xC0, 0x1F, 0x80, 0x0F, 0x00, 0x00, // Code for char b
0x0A, 0x80, 0x0F, 0xC0, 0x1F, 0xE0, 0x38, 0x60, 0x30, 0x60, 0x30, 0x60, 0x30, 0x60, 0x30, 0x60, 0x30, 0xC0, 0x18, 0x80, 0x08, 0x00, 0x00, // Code for char c
0x0A, 0x80, 0x0F, 0xC0, 0x1F, 0xE0, 0x38, 0x60, 0x30, 0x60, 0x30, 0x60, 0x30, 0xE0, 0x30, 0xC0, 0x30, 0xFF, 0x3F, 0xFF, 0x3F, 0x00, 0x00, // Code for char d
0x0A, 0x80, 0x0F, 0xC0, 0x1F, 0xE0, 0x3B, 0x60, 0x33, 0x60, 0x33, 0x60, 0x33, 0x60, 0x33, 0x60, 0x33, 0xC0, 0x13, 0x80, 0x01, 0x00, 0x00, // Code for char e
0x08, 0xC0, 0x00, 0xC0, 0x00, 0xFC, 0x3F, 0xFE, 0x3F, 0xC7, 0x00, 0xC3, 0x00, 0xC3, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char f
0x0A, 0x80, 0x03, 0xC0, 0xC7, 0xE0, 0xCE, 0x60, 0xCC, 0x60, 0xCC, 0x60, 0xCC, 0x60, 0xCC, 0x60, 0xE6, 0xE0, 0x7F, 0xE0, 0x3F, 0x00, 0x00, // Code for char g
0x09, 0xFF, 0x3F, 0xFF, 0x3F, 0xC0, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0xE0, 0x00, 0xC0, 0x3F, 0x80, 0x3F, 0x00, 0x00, 0x00, 0x00, // Code for char h
0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x60, 0x30, 0xEC, 0x3F, 0xEC, 0x3F, 0x00, 0x30, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char i
0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0xE0, 0x00, 0xC0, 0x60, 0xC0, 0xEC, 0xFF, 0xEC, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char j
0x09, 0x00, 0x00, 0xFF, 0x3F, 0xFF, 0x3F, 0x00, 0x03, 0x80, 0x07, 0xC0, 0x0F, 0xE0, 0x1C, 0x60, 0x38, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, // Code for char k
0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x03, 0x30, 0xFF, 0x3F, 0xFF, 0x3F, 0x00, 0x30, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char l
0x0A, 0xE0, 0x3F, 0xC0, 0x3F, 0xE0, 0x00, 0xE0, 0x00, 0xC0, 0x3F, 0xC0, 0x3F, 0xE0, 0x00, 0xE0, 0x00, 0xC0, 0x3F, 0x80, 0x3F, 0x00, 0x00, // Code for char m
0x0A, 0x00, 0x00, 0xE0, 0x3F, 0xE0, 0x3F, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0xE0, 0x00, 0xC0, 0x3F, 0x80, 0x3F, 0x00, 0x00, // Code for char n
0x0A, 0x80, 0x0F, 0xC0, 0x1F, 0xE0, 0x38, 0x60, 0x30, 0x60, 0x30, 0x60, 0x30, 0x60, 0x30, 0xE0, 0x38, 0xC0, 0x1F, 0x80, 0x0F, 0x00, 0x00, // Code for char o
0x0A, 0xE0, 0xFF, 0xE0, 0xFF, 0x60, 0x0C, 0x60, 0x18, 0x60, 0x18, 0x60, 0x18, 0x60, 0x18, 0xE0, 0x1C, 0xC0, 0x0F, 0x80, 0x07, 0x00, 0x00, // Code for char p
0x0A, 0x80, 0x07, 0xC0, 0x0F, 0xE0, 0x1C, 0x60, 0x18, 0x60, 0x18, 0x60, 0x18, 0x60, 0x18, 0x60, 0x0C, 0xE0, 0xFF, 0xE0, 0xFF, 0x00, 0x00, // Code for char q
0x0A, 0x00, 0x00, 0xE0, 0x3F, 0xE0, 0x3F, 0xC0, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0xE0, 0x00, 0xC0, 0x00, 0x00, 0x00, // Code for char r
0x08, 0xC0, 0x11, 0xE0, 0x33, 0x60, 0x33, 0x60, 0x33, 0x60, 0x33, 0x60, 0x33, 0x60, 0x3F, 0x40, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char s
0x08, 0x60, 0x00, 0x60, 0x00, 0xFE, 0x1F, 0xFE, 0x3F, 0x60, 0x30, 0x60, 0x30, 0x60, 0x30, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char t
0x0A, 0xE0, 0x0F, 0xE0, 0x1F, 0x00, 0x38, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x18, 0xE0, 0x3F, 0xE0, 0x3F, 0x00, 0x00, // Code for char u
0x0A, 0x60, 0x00, 0xE0, 0x01, 0x80, 0x07, 0x00, 0x1E, 0x00, 0x38, 0x00, 0x38, 0x00, 0x1E, 0x80, 0x07, 0xE0, 0x01, 0x60, 0x00, 0x00, 0x00, // Code for char v
0x0A, 0xE0, 0x07, 0xE0, 0x1F, 0x00, 0x38, 0x00, 0x1C, 0xE0, 0x0F, 0xE0, 0x0F, 0x00, 0x1C, 0x00, 0x38, 0xE0, 0x1F, 0xE0, 0x07, 0x00, 0x00, // Code for char w
0x09, 0x60, 0x30, 0xE0, 0x38, 0xC0, 0x1D, 0x80, 0x0F, 0x00, 0x07, 0x80, 0x0F, 0xC0, 0x1D, 0xE0, 0x38, 0x60, 0x30, 0x00, 0x00, 0x00, 0x00, // Code for char x
0x09, 0x00, 0x00, 0x60, 0x00, 0xE0, 0x81, 0x80, 0xE7, 0x00, 0x7E, 0x00, 0x1E, 0x80, 0x07, 0xE0, 0x01, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char y
0x09, 0x60, 0x30, 0x60, 0x38, 0x60, 0x3C, 0x60, 0x36, 0x60, 0x33, 0xE0, 0x31, 0xE0, 0x30, 0x60, 0x30, 0x20, 0x30, 0x00, 0x00, 0x00, 0x00, // Code for char z
0x09, 0x00, 0x00, 0x80, 0x00, 0xC0, 0x01, 0xFC, 0x1F, 0x7E, 0x3F, 0x07, 0x70, 0x03, 0x60, 0x03, 0x60, 0x03, 0x60, 0x00, 0x00, 0x00, 0x00, // Code for char {
0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x3F, 0xFF, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char |
0x09, 0x00, 0x00, 0x03, 0x60, 0x03, 0x60, 0x03, 0x60, 0x07, 0x70, 0x7E, 0x3F, 0xFC, 0x1F, 0xC0, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char }
0x0A, 0x10, 0x00, 0x18, 0x00, 0x0C, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x18, 0x00, 0x10, 0x00, 0x18, 0x00, 0x0C, 0x00, 0x04, 0x00, 0x00, 0x00, // Code for char ~
0x0A, 0x00, 0x0F, 0x80, 0x0F, 0xC0, 0x0C, 0x60, 0x0C, 0x30, 0x0C, 0x30, 0x0C, 0x60, 0x0C, 0xC0, 0x0C, 0x80, 0x0F, 0x00, 0x0F, 0x00, 0x00 // Code for char 
};
//Font Generated by MikroElektronika GLCD Font Creator 1.2.0.0
//MikroElektronika 2011
//http://www.mikroe.com
//GLCD FontName : Terminal12x16
//GLCD FontSize : 12 x 16
fontdatatype Terminal12x16[] PROGMEM = {
0x0C, 0x10, 0x20, 0x60,
0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char
0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0x00, 0xFF, 0x33, 0xFF, 0x33, 0xFF, 0x33, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char !
0x09, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x3C, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x3C, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char "
0x0C, 0x00, 0x02, 0x10, 0x1E, 0x90, 0x1F, 0xF0, 0x1F, 0xFE, 0x03, 0x7E, 0x1E, 0x9E, 0x1F, 0xF0, 0x1F, 0xFE, 0x03, 0x7E, 0x02, 0x1E, 0x00, 0x10, 0x00, // Code for char #
0x0A, 0x00, 0x00, 0x78, 0x04, 0xFC, 0x0C, 0xFC, 0x0C, 0xFF, 0x3F, 0xFF, 0x3F, 0xFF, 0x3F, 0xCC, 0x0F, 0xCC, 0x0F, 0x88, 0x07, 0x00, 0x00, 0x00, 0x00, // Code for char $
0x0C, 0x00, 0x30, 0x38, 0x38, 0x38, 0x3C, 0x38, 0x1E, 0x38, 0x0F, 0x80, 0x07, 0xC0, 0x03, 0xE0, 0x39, 0xF0, 0x38, 0x78, 0x38, 0x3C, 0x38, 0x1C, 0x00, // Code for char %
0x0B, 0x00, 0x00, 0x00, 0x1F, 0xB8, 0x3F, 0xFC, 0x3F, 0xFE, 0x31, 0xE6, 0x37, 0xFE, 0x3F, 0x3E, 0x1E, 0x1C, 0x3E, 0x00, 0x36, 0x00, 0x22, 0x00, 0x00, // Code for char &
0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x27, 0x00, 0x3F, 0x00, 0x3F, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char '
0x09, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x03, 0xFC, 0x0F, 0xFE, 0x1F, 0xFF, 0x3F, 0x07, 0x38, 0x01, 0x20, 0x01, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char (
0x09, 0x00, 0x00, 0x00, 0x00, 0x01, 0x20, 0x01, 0x20, 0x07, 0x38, 0xFF, 0x3F, 0xFE, 0x1F, 0xFC, 0x0F, 0xF0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char )
0x0A, 0x00, 0x00, 0x98, 0x0C, 0xB8, 0x0E, 0xF8, 0x0F, 0xF8, 0x0F, 0xF8, 0x0F, 0xF8, 0x0F, 0xF8, 0x0F, 0xB8, 0x0E, 0x98, 0x0C, 0x00, 0x00, 0x00, 0x00, // Code for char *
0x0A, 0x00, 0x00, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0xF0, 0x0F, 0xF0, 0x0F, 0xF0, 0x0F, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, // Code for char +
0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB8, 0x00, 0xF8, 0x00, 0xF8, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ,
0x0A, 0x00, 0x00, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, // Code for char -
0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char .
0x0C, 0x00, 0x18, 0x00, 0x1C, 0x00, 0x1E, 0x00, 0x0F, 0x80, 0x07, 0xC0, 0x03, 0xE0, 0x01, 0xF0, 0x00, 0x78, 0x00, 0x3C, 0x00, 0x1E, 0x00, 0x0E, 0x00, // Code for char /
0x0C, 0xF8, 0x07, 0xFE, 0x1F, 0xFE, 0x1F, 0x07, 0x3F, 0x83, 0x33, 0xC3, 0x31, 0xE3, 0x30, 0x73, 0x30, 0x3F, 0x38, 0xFE, 0x1F, 0xFE, 0x1F, 0xF8, 0x07, // Code for char 0
0x0B, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x30, 0x0C, 0x30, 0x0E, 0x30, 0xFF, 0x3F, 0xFF, 0x3F, 0xFF, 0x3F, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x00, // Code for char 1
0x0C, 0x1C, 0x30, 0x1E, 0x38, 0x1F, 0x3C, 0x07, 0x3E, 0x03, 0x3F, 0x83, 0x37, 0xC3, 0x33, 0xE3, 0x31, 0xF7, 0x30, 0x7F, 0x30, 0x3E, 0x30, 0x1C, 0x30, // Code for char 2
0x0C, 0x0C, 0x0C, 0x0E, 0x1C, 0x0F, 0x3C, 0xC7, 0x38, 0xC3, 0x30, 0xC3, 0x30, 0xC3, 0x30, 0xC3, 0x30, 0xE7, 0x39, 0xFF, 0x3F, 0x7E, 0x1F, 0x3C, 0x0E, // Code for char 3
0x0C, 0xC0, 0x03, 0xE0, 0x03, 0xF0, 0x03, 0x78, 0x03, 0x3C, 0x03, 0x1E, 0x03, 0x0F, 0x03, 0xFF, 0x3F, 0xFF, 0x3F, 0xFF, 0x3F, 0x00, 0x03, 0x00, 0x03, // Code for char 4
0x0C, 0x3F, 0x0C, 0x7F, 0x1C, 0x7F, 0x3C, 0x63, 0x38, 0x63, 0x30, 0x63, 0x30, 0x63, 0x30, 0x63, 0x30, 0xE3, 0x38, 0xE3, 0x3F, 0xC3, 0x1F, 0x83, 0x0F, // Code for char 5
0x0C, 0xC0, 0x0F, 0xF0, 0x1F, 0xF8, 0x3F, 0xFC, 0x39, 0xDE, 0x30, 0xCF, 0x30, 0xC7, 0x30, 0xC3, 0x30, 0xC3, 0x39, 0xC3, 0x3F, 0x80, 0x1F, 0x00, 0x0F, // Code for char 6
0x0C, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x30, 0x03, 0x3C, 0x03, 0x3F, 0xC3, 0x0F, 0xF3, 0x03, 0xFF, 0x00, 0x3F, 0x00, 0x0F, 0x00, 0x03, 0x00, // Code for char 7
0x0C, 0x00, 0x0F, 0xBC, 0x1F, 0xFE, 0x3F, 0xFF, 0x39, 0xE7, 0x30, 0xC3, 0x30, 0xC3, 0x30, 0xE7, 0x30, 0xFF, 0x39, 0xFE, 0x3F, 0xBC, 0x1F, 0x00, 0x0F, // Code for char 8
0x0C, 0x3C, 0x00, 0x7E, 0x00, 0xFF, 0x30, 0xE7, 0x30, 0xC3, 0x30, 0xC3, 0x38, 0xC3, 0x3C, 0xC3, 0x1E, 0xE7, 0x0F, 0xFF, 0x07, 0xFE, 0x03, 0xFC, 0x00, // Code for char 9
0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x1C, 0x70, 0x1C, 0x70, 0x1C, 0x70, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char :
0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x9C, 0x70, 0xFC, 0x70, 0xFC, 0x70, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ;
0x0A, 0x00, 0x00, 0xC0, 0x00, 0xE0, 0x01, 0xF0, 0x03, 0xF8, 0x07, 0x3C, 0x0F, 0x1E, 0x1E, 0x0F, 0x3C, 0x07, 0x38, 0x03, 0x30, 0x00, 0x00, 0x00, 0x00, // Code for char <
0x0B, 0x00, 0x00, 0x60, 0x06, 0x60, 0x06, 0x60, 0x06, 0x60, 0x06, 0x60, 0x06, 0x60, 0x06, 0x60, 0x06, 0x60, 0x06, 0x60, 0x06, 0x60, 0x06, 0x00, 0x00, // Code for char =
0x0A, 0x00, 0x00, 0x03, 0x30, 0x07, 0x38, 0x0F, 0x3C, 0x1E, 0x1E, 0x3C, 0x0F, 0xF8, 0x07, 0xF0, 0x03, 0xE0, 0x01, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char >
0x0B, 0x1C, 0x00, 0x1E, 0x00, 0x1F, 0x00, 0x07, 0x00, 0x83, 0x37, 0xC3, 0x37, 0xE3, 0x37, 0xF7, 0x00, 0x7F, 0x00, 0x3E, 0x00, 0x1C, 0x00, 0x00, 0x00, // Code for char ?
0x0C, 0xF8, 0x0F, 0xFE, 0x1F, 0xFF, 0x1F, 0xF7, 0x3B, 0xFB, 0x37, 0xFB, 0x37, 0xFB, 0x37, 0xFB, 0x37, 0xFF, 0x37, 0xFF, 0x37, 0xFE, 0x03, 0xF8, 0x01, // Code for char @
0x0B, 0x00, 0x38, 0x00, 0x3F, 0xE0, 0x3F, 0xFC, 0x07, 0xFF, 0x06, 0x1F, 0x06, 0xFF, 0x06, 0xFC, 0x07, 0xE0, 0x3F, 0x00, 0x3F, 0x00, 0x38, 0x00, 0x00, // Code for char A
0x0B, 0xFF, 0x3F, 0xFF, 0x3F, 0xFF, 0x3F, 0xC3, 0x30, 0xC3, 0x30, 0xC3, 0x30, 0xE7, 0x30, 0xFF, 0x39, 0xFE, 0x3F, 0xBC, 0x1F, 0x00, 0x0F, 0x00, 0x00, // Code for char B
0x0B, 0xF0, 0x03, 0xFC, 0x0F, 0xFE, 0x1F, 0x0F, 0x3C, 0x07, 0x38, 0x03, 0x30, 0x03, 0x30, 0x07, 0x38, 0x0F, 0x3C, 0x0E, 0x1C, 0x0C, 0x0C, 0x00, 0x00, // Code for char C
0x0B, 0xFF, 0x3F, 0xFF, 0x3F, 0xFF, 0x3F, 0x03, 0x30, 0x03, 0x30, 0x03, 0x30, 0x07, 0x38, 0x0F, 0x3C, 0xFE, 0x1F, 0xFC, 0x0F, 0xF0, 0x03, 0x00, 0x00, // Code for char D
0x0B, 0xFF, 0x3F, 0xFF, 0x3F, 0xFF, 0x3F, 0xC3, 0x30, 0xC3, 0x30, 0xC3, 0x30, 0xC3, 0x30, 0xC3, 0x30, 0xC3, 0x30, 0x03, 0x30, 0x03, 0x30, 0x00, 0x00, // Code for char E
0x0B, 0xFF, 0x3F, 0xFF, 0x3F, 0xFF, 0x3F, 0xC3, 0x00, 0xC3, 0x00, 0xC3, 0x00, 0xC3, 0x00, 0xC3, 0x00, 0xC3, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, // Code for char F
0x0B, 0xF0, 0x03, 0xFC, 0x0F, 0xFE, 0x1F, 0x0F, 0x3C, 0x07, 0x38, 0xC3, 0x30, 0xC3, 0x30, 0xC3, 0x30, 0xC7, 0x3F, 0xC7, 0x3F, 0xC6, 0x3F, 0x00, 0x00, // Code for char G
0x0B, 0xFF, 0x3F, 0xFF, 0x3F, 0xFF, 0x3F, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xFF, 0x3F, 0xFF, 0x3F, 0xFF, 0x3F, 0x00, 0x00, // Code for char H
0x09, 0x00, 0x00, 0x00, 0x00, 0x03, 0x30, 0x03, 0x30, 0xFF, 0x3F, 0xFF, 0x3F, 0xFF, 0x3F, 0x03, 0x30, 0x03, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char I
0x0B, 0x00, 0x0E, 0x00, 0x1E, 0x00, 0x3E, 0x00, 0x38, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x38, 0xFF, 0x3F, 0xFF, 0x1F, 0xFF, 0x07, 0x00, 0x00, // Code for char J
0x0B, 0xFF, 0x3F, 0xFF, 0x3F, 0xFF, 0x3F, 0xE0, 0x01, 0xF0, 0x03, 0xF8, 0x07, 0x3C, 0x0F, 0x1E, 0x1E, 0x0F, 0x3C, 0x07, 0x38, 0x03, 0x30, 0x00, 0x00, // Code for char K
0x0B, 0xFF, 0x3F, 0xFF, 0x3F, 0xFF, 0x3F, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x00, // Code for char L
0x0B, 0xFF, 0x3F, 0xFF, 0x3F, 0xFF, 0x3F, 0x7E, 0x00, 0xF8, 0x01, 0xE0, 0x01, 0xF8, 0x01, 0x7E, 0x00, 0xFF, 0x3F, 0xFF, 0x3F, 0xFF, 0x3F, 0x00, 0x00, // Code for char M
0x0B, 0xFF, 0x3F, 0xFF, 0x3F, 0xFF, 0x3F, 0x3E, 0x00, 0xF8, 0x00, 0xF0, 0x03, 0xC0, 0x07, 0x00, 0x1F, 0xFF, 0x3F, 0xFF, 0x3F, 0xFF, 0x3F, 0x00, 0x00, // Code for char N
0x0B, 0xF0, 0x03, 0xFC, 0x0F, 0xFE, 0x1F, 0x0F, 0x3C, 0x07, 0x38, 0x03, 0x30, 0x07, 0x38, 0x0F, 0x3C, 0xFE, 0x1F, 0xFC, 0x0F, 0xF0, 0x03, 0x00, 0x00, // Code for char O
0x0B, 0xFF, 0x3F, 0xFF, 0x3F, 0xFF, 0x3F, 0x83, 0x01, 0x83, 0x01, 0x83, 0x01, 0x83, 0x01, 0xC7, 0x01, 0xFF, 0x01, 0xFE, 0x00, 0x7C, 0x00, 0x00, 0x00, // Code for char P
0x0B, 0xF0, 0x03, 0xFC, 0x0F, 0xFE, 0x1F, 0x0F, 0x3C, 0x07, 0x38, 0x03, 0x36, 0x07, 0x3E, 0x0F, 0x3E, 0xFE, 0x3F, 0xFC, 0x3F, 0xF0, 0x33, 0x00, 0x00, // Code for char Q
0x0B, 0xFF, 0x3F, 0xFF, 0x3F, 0xFF, 0x3F, 0x83, 0x01, 0x83, 0x03, 0x83, 0x07, 0x83, 0x0F, 0xC7, 0x1F, 0xFF, 0x3D, 0xFE, 0x38, 0x7C, 0x30, 0x00, 0x00, // Code for char R
0x0B, 0x3C, 0x0C, 0x7E, 0x1C, 0xFF, 0x3C, 0xE7, 0x38, 0xC3, 0x30, 0xC3, 0x30, 0xC3, 0x30, 0xC7, 0x39, 0xCF, 0x3F, 0x8E, 0x1F, 0x0C, 0x0F, 0x00, 0x00, // Code for char S
0x0A, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0xFF, 0x3F, 0xFF, 0x3F, 0xFF, 0x3F, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char T
0x0B, 0xFF, 0x07, 0xFF, 0x1F, 0xFF, 0x3F, 0x00, 0x38, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x38, 0xFF, 0x3F, 0xFF, 0x1F, 0xFF, 0x07, 0x00, 0x00, // Code for char U
0x0B, 0x07, 0x00, 0x3F, 0x00, 0xFF, 0x01, 0xF8, 0x0F, 0xC0, 0x3F, 0x00, 0x3E, 0xC0, 0x3F, 0xF8, 0x0F, 0xFF, 0x01, 0x3F, 0x00, 0x07, 0x00, 0x00, 0x00, // Code for char V
0x0B, 0xFF, 0x3F, 0xFF, 0x3F, 0xFF, 0x3F, 0x00, 0x1E, 0x80, 0x07, 0x80, 0x03, 0x80, 0x07, 0x00, 0x1E, 0xFF, 0x3F, 0xFF, 0x3F, 0xFF, 0x3F, 0x00, 0x00, // Code for char W
0x0B, 0x03, 0x30, 0x0F, 0x3C, 0x1F, 0x3E, 0x3C, 0x0F, 0xF0, 0x03, 0xE0, 0x01, 0xF0, 0x03, 0x3C, 0x0F, 0x1F, 0x3E, 0x0F, 0x3C, 0x03, 0x30, 0x00, 0x00, // Code for char X
0x0B, 0x03, 0x00, 0x0F, 0x00, 0x3F, 0x00, 0xFC, 0x00, 0xF0, 0x3F, 0xC0, 0x3F, 0xF0, 0x3F, 0xFC, 0x00, 0x3F, 0x00, 0x0F, 0x00, 0x03, 0x00, 0x00, 0x00, // Code for char Y
0x0B, 0x03, 0x30, 0x03, 0x3C, 0x03, 0x3E, 0x03, 0x3F, 0xC3, 0x33, 0xE3, 0x31, 0xF3, 0x30, 0x3F, 0x30, 0x1F, 0x30, 0x0F, 0x30, 0x03, 0x30, 0x00, 0x00, // Code for char Z
0x09, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x3F, 0xFF, 0x3F, 0xFF, 0x3F, 0x03, 0x30, 0x03, 0x30, 0x03, 0x30, 0x03, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char [
0x0C, 0x0E, 0x00, 0x1E, 0x00, 0x3C, 0x00, 0x78, 0x00, 0xF0, 0x00, 0xE0, 0x01, 0xC0, 0x03, 0x80, 0x07, 0x00, 0x0F, 0x00, 0x1E, 0x00, 0x1C, 0x00, 0x18, // Code for char BackSlash
0x09, 0x00, 0x00, 0x00, 0x00, 0x03, 0x30, 0x03, 0x30, 0x03, 0x30, 0x03, 0x30, 0xFF, 0x3F, 0xFF, 0x3F, 0xFF, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ]
0x0C, 0x60, 0x00, 0x70, 0x00, 0x78, 0x00, 0x3C, 0x00, 0x1E, 0x00, 0x0F, 0x00, 0x0F, 0x00, 0x1E, 0x00, 0x3C, 0x00, 0x78, 0x00, 0x70, 0x00, 0x60, 0x00, // Code for char ^
0x0C, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, // Code for char _
0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x7E, 0x00, 0x7E, 0x00, 0x4E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char `
0x0B, 0x00, 0x1C, 0x40, 0x3E, 0x60, 0x3F, 0x60, 0x33, 0x60, 0x33, 0x60, 0x33, 0x60, 0x33, 0x60, 0x33, 0xE0, 0x3F, 0xE0, 0x3F, 0xC0, 0x3F, 0x00, 0x00, // Code for char a
0x0B, 0xFF, 0x3F, 0xFF, 0x3F, 0xFF, 0x3F, 0xE0, 0x30, 0x60, 0x30, 0x60, 0x30, 0x60, 0x30, 0xE0, 0x38, 0xE0, 0x3F, 0xC0, 0x1F, 0x80, 0x0F, 0x00, 0x00, // Code for char b
0x0B, 0x80, 0x0F, 0xC0, 0x1F, 0xE0, 0x3F, 0xE0, 0x38, 0x60, 0x30, 0x60, 0x30, 0x60, 0x30, 0x60, 0x30, 0xE0, 0x38, 0xC0, 0x18, 0x80, 0x08, 0x00, 0x00, // Code for char c
0x0B, 0x80, 0x0F, 0xC0, 0x1F, 0xE0, 0x3F, 0xE0, 0x38, 0x60, 0x30, 0x60, 0x30, 0xE0, 0x30, 0xE0, 0x30, 0xFF, 0x3F, 0xFF, 0x3F, 0xFF, 0x3F, 0x00, 0x00, // Code for char d
0x0B, 0x80, 0x0F, 0xC0, 0x1F, 0xE0, 0x3F, 0xE0, 0x3B, 0x60, 0x33, 0x60, 0x33, 0x60, 0x33, 0x60, 0x33, 0xE0, 0x33, 0xC0, 0x13, 0x80, 0x01, 0x00, 0x00, // Code for char e
0x09, 0xC0, 0x00, 0xC0, 0x00, 0xFC, 0x3F, 0xFE, 0x3F, 0xFF, 0x3F, 0xC7, 0x00, 0xC3, 0x00, 0xC3, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char f
0x0B, 0x80, 0x03, 0xC0, 0xC7, 0xE0, 0xCF, 0xE0, 0xCE, 0x60, 0xCC, 0x60, 0xCC, 0x60, 0xCC, 0x60, 0xEE, 0xE0, 0xFF, 0xE0, 0x7F, 0xE0, 0x3F, 0x00, 0x00, // Code for char g
0x0A, 0xFF, 0x3F, 0xFF, 0x3F, 0xFF, 0x3F, 0xE0, 0x00, 0x60, 0x00, 0x60, 0x00, 0xE0, 0x00, 0xE0, 0x3F, 0xC0, 0x3F, 0x80, 0x3F, 0x00, 0x00, 0x00, 0x00, // Code for char h
0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x60, 0x30, 0xEC, 0x3F, 0xEC, 0x3F, 0xEC, 0x3F, 0x00, 0x30, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char i
0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0xE0, 0x00, 0xE0, 0x60, 0xC0, 0xEC, 0xFF, 0xEC, 0xFF, 0xEC, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char j
0x0A, 0x00, 0x00, 0xFF, 0x3F, 0xFF, 0x3F, 0xFF, 0x3F, 0x80, 0x07, 0xC0, 0x0F, 0xE0, 0x1F, 0xE0, 0x3C, 0x60, 0x38, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, // Code for char k
0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x03, 0x30, 0xFF, 0x3F, 0xFF, 0x3F, 0xFF, 0x3F, 0x00, 0x30, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char l
0x0B, 0xE0, 0x3F, 0xE0, 0x3F, 0xE0, 0x3F, 0xE0, 0x00, 0xE0, 0x3F, 0xC0, 0x3F, 0xE0, 0x3F, 0xE0, 0x00, 0xE0, 0x3F, 0xC0, 0x3F, 0x80, 0x3F, 0x00, 0x00, // Code for char m
0x0B, 0x00, 0x00, 0xE0, 0x3F, 0xE0, 0x3F, 0xE0, 0x3F, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0xE0, 0x00, 0xE0, 0x3F, 0xC0, 0x3F, 0x80, 0x3F, 0x00, 0x00, // Code for char n
0x0B, 0x80, 0x0F, 0xC0, 0x1F, 0xE0, 0x3F, 0xE0, 0x38, 0x60, 0x30, 0x60, 0x30, 0x60, 0x30, 0xE0, 0x38, 0xE0, 0x3F, 0xC0, 0x1F, 0x80, 0x0F, 0x00, 0x00, // Code for char o
0x0B, 0xE0, 0xFF, 0xE0, 0xFF, 0xE0, 0xFF, 0x60, 0x1C, 0x60, 0x18, 0x60, 0x18, 0x60, 0x18, 0xE0, 0x1C, 0xE0, 0x1F, 0xC0, 0x0F, 0x80, 0x07, 0x00, 0x00, // Code for char p
0x0B, 0x80, 0x07, 0xC0, 0x0F, 0xE0, 0x1F, 0xE0, 0x1C, 0x60, 0x18, 0x60, 0x18, 0x60, 0x18, 0x60, 0x1C, 0xE0, 0xFF, 0xE0, 0xFF, 0xE0, 0xFF, 0x00, 0x00, // Code for char q
0x0B, 0x00, 0x00, 0xE0, 0x3F, 0xE0, 0x3F, 0xE0, 0x3F, 0xE0, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0xE0, 0x00, 0xE0, 0x00, 0xC0, 0x00, 0x00, 0x00, // Code for char r
0x09, 0xC0, 0x11, 0xE0, 0x33, 0xE0, 0x33, 0x60, 0x33, 0x60, 0x33, 0x60, 0x33, 0x60, 0x3F, 0x60, 0x3F, 0x40, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char s
0x09, 0x60, 0x00, 0x60, 0x00, 0xFE, 0x1F, 0xFE, 0x3F, 0xFE, 0x3F, 0x60, 0x30, 0x60, 0x30, 0x60, 0x30, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char t
0x0B, 0xE0, 0x0F, 0xE0, 0x1F, 0xE0, 0x3F, 0x00, 0x38, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x38, 0xE0, 0x3F, 0xE0, 0x3F, 0xE0, 0x3F, 0x00, 0x00, // Code for char u
0x0B, 0x60, 0x00, 0xE0, 0x01, 0xE0, 0x07, 0x80, 0x1F, 0x00, 0x3E, 0x00, 0x38, 0x00, 0x3E, 0x80, 0x1F, 0xE0, 0x07, 0xE0, 0x01, 0x60, 0x00, 0x00, 0x00, // Code for char v
0x0B, 0xE0, 0x07, 0xE0, 0x1F, 0xE0, 0x3F, 0x00, 0x3C, 0xE0, 0x1F, 0xE0, 0x0F, 0xE0, 0x1F, 0x00, 0x3C, 0xE0, 0x3F, 0xE0, 0x1F, 0xE0, 0x07, 0x00, 0x00, // Code for char w
0x0A, 0x60, 0x30, 0xE0, 0x38, 0xE0, 0x3D, 0xC0, 0x1F, 0x80, 0x0F, 0x80, 0x0F, 0xC0, 0x1F, 0xE0, 0x3D, 0xE0, 0x38, 0x60, 0x30, 0x00, 0x00, 0x00, 0x00, // Code for char x
0x0A, 0x00, 0x00, 0x60, 0x00, 0xE0, 0x81, 0xE0, 0xE7, 0x80, 0xFF, 0x00, 0x7E, 0x80, 0x1F, 0xE0, 0x07, 0xE0, 0x01, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char y
0x0A, 0x60, 0x30, 0x60, 0x38, 0x60, 0x3C, 0x60, 0x3E, 0x60, 0x37, 0xE0, 0x33, 0xE0, 0x31, 0xE0, 0x30, 0x60, 0x30, 0x20, 0x30, 0x00, 0x00, 0x00, 0x00, // Code for char z
0x0A, 0x00, 0x00, 0x80, 0x00, 0xC0, 0x01, 0xFC, 0x1F, 0xFE, 0x3F, 0x7F, 0x7F, 0x07, 0x70, 0x03, 0x60, 0x03, 0x60, 0x03, 0x60, 0x00, 0x00, 0x00, 0x00, // Code for char {
0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x3F, 0xFF, 0x3F, 0xFF, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char |
0x0A, 0x00, 0x00, 0x03, 0x60, 0x03, 0x60, 0x03, 0x60, 0x07, 0x70, 0x7F, 0x7F, 0xFE, 0x3F, 0xFC, 0x1F, 0xC0, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char }
0x0B, 0x10, 0x00, 0x18, 0x00, 0x1C, 0x00, 0x0C, 0x00, 0x0C, 0x00, 0x1C, 0x00, 0x18, 0x00, 0x18, 0x00, 0x1C, 0x00, 0x0C, 0x00, 0x04, 0x00, 0x00, 0x00, // Code for char ~
0x0B, 0x00, 0x0F, 0x80, 0x0F, 0xC0, 0x0F, 0xE0, 0x0C, 0x70, 0x0C, 0x30, 0x0C, 0x70, 0x0C, 0xE0, 0x0C, 0xC0, 0x0F, 0x80, 0x0F, 0x00, 0x0F, 0x00, 0x00 // Code for char 
};
//Font Generated by MikroElektronika GLCD Font Creator 1.2.0.0
//MikroElektronika 2011
//http://www.mikroe.com
//GLCD FontName : Trebuchet_MS16x21
//GLCD FontSize : 16 x 21
fontdatatype Trebuchet_MS16x21[] PROGMEM = {
0x10, 0x15, 0x2E, 0x0D,
0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char .
0x0C, 0x00, 0x00, 0x10, 0x00, 0x00, 0x1E, 0x00, 0xC0, 0x1F, 0x00, 0xF0, 0x1F, 0x00, 0xFE, 0x0F, 0x80, 0xFF, 0x03, 0xF0, 0x7F, 0x00, 0xFE, 0x0F, 0x00, 0xFF, 0x03, 0x00, 0x7F, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char /
0x10, 0x00, 0x00, 0x00, 0xC0, 0x7F, 0x00, 0xF0, 0xFF, 0x03, 0xFC, 0xFF, 0x07, 0xFE, 0xFF, 0x0F, 0x3E, 0x80, 0x0F, 0x0F, 0x00, 0x1E, 0x07, 0x00, 0x1C, 0x07, 0x00, 0x1C, 0x07, 0x00, 0x1C, 0x0F, 0x00, 0x1E, 0x1F, 0x80, 0x0F, 0xFE, 0xFF, 0x0F, 0xFC, 0xFF, 0x07, 0xF8, 0xFF, 0x01, 0xC0, 0x7F, 0x00, // Code for char 0
0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x78, 0x00, 0x00, 0x38, 0x00, 0x00, 0x3C, 0x00, 0x00, 0xFE, 0xFF, 0x1F, 0xFE, 0xFF, 0x1F, 0xFF, 0xFF, 0x1F, 0xFF, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 1
0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x18, 0x1E, 0x00, 0x1C, 0x0E, 0x00, 0x1F, 0x0F, 0x80, 0x1F, 0x07, 0xC0, 0x1F, 0x07, 0xF0, 0x1F, 0x07, 0xF8, 0x1D, 0x07, 0xFE, 0x1C, 0x0F, 0x3F, 0x1C, 0xFE, 0x1F, 0x1C, 0xFE, 0x0F, 0x1C, 0xFC, 0x03, 0x1C, 0xF8, 0x00, 0x1C, 0x00, 0x00, 0x1C, // Code for char 2
0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x04, 0x00, 0x0E, 0x0E, 0x00, 0x0F, 0x0F, 0x00, 0x1E, 0x07, 0x00, 0x1C, 0x07, 0x07, 0x1C, 0x07, 0x07, 0x1C, 0x07, 0x07, 0x1C, 0x8F, 0x0F, 0x1E, 0xFF, 0x1F, 0x1E, 0xFE, 0xFD, 0x0F, 0xFC, 0xFD, 0x0F, 0x78, 0xF8, 0x07, 0x00, 0xF0, 0x01, 0x00, 0x00, 0x00, // Code for char 3
0x10, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x00, 0xF0, 0x00, 0x00, 0xF8, 0x00, 0x00, 0xFE, 0x00, 0x00, 0xEF, 0x00, 0x80, 0xE7, 0x00, 0xC0, 0xE3, 0x00, 0xF0, 0xE0, 0x00, 0x78, 0xE0, 0x00, 0xFC, 0xFF, 0x1F, 0xFE, 0xFF, 0x1F, 0xFF, 0xFF, 0x1F, 0xFF, 0xFF, 0x1F, 0x00, 0xE0, 0x00, 0x00, 0xE0, 0x00, // Code for char 4
0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0xFF, 0x03, 0x0F, 0xFF, 0x07, 0x0E, 0xFF, 0x03, 0x1C, 0xFF, 0x01, 0x1C, 0xC7, 0x01, 0x1C, 0xC7, 0x01, 0x1C, 0xC7, 0x01, 0x1E, 0xC7, 0x03, 0x1F, 0xC7, 0xFF, 0x0F, 0x87, 0xFF, 0x0F, 0x07, 0xFF, 0x07, 0x00, 0xFC, 0x01, 0x00, 0x00, 0x00, // Code for char 5
0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x80, 0xFF, 0x03, 0xE0, 0xFF, 0x07, 0xF0, 0xFF, 0x0F, 0xF8, 0x0F, 0x1E, 0x7C, 0x07, 0x1C, 0x3E, 0x07, 0x1C, 0x1E, 0x07, 0x1C, 0x0F, 0x07, 0x1C, 0x07, 0x0F, 0x1E, 0x02, 0xFE, 0x0F, 0x00, 0xFE, 0x0F, 0x00, 0xFC, 0x07, 0x00, 0xF0, 0x01, // Code for char 6
0x10, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x18, 0x07, 0x00, 0x1E, 0x07, 0xC0, 0x1F, 0x07, 0xF0, 0x1F, 0x07, 0xFC, 0x0F, 0x07, 0xFF, 0x01, 0xC7, 0x7F, 0x00, 0xF7, 0x0F, 0x00, 0xFF, 0x03, 0x00, 0xFF, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x03, 0x00, 0x00, // Code for char 7
0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x03, 0x78, 0xF8, 0x07, 0xFC, 0xFD, 0x0F, 0xFE, 0xFF, 0x0F, 0xFF, 0x1F, 0x1E, 0x8F, 0x0F, 0x1C, 0x07, 0x07, 0x1C, 0x07, 0x0F, 0x1C, 0x8F, 0x0F, 0x1C, 0xFF, 0x3F, 0x1E, 0xFE, 0xFD, 0x0F, 0xFE, 0xFD, 0x0F, 0x78, 0xF0, 0x07, 0x00, 0xE0, 0x03, // Code for char 8
0x0F, 0x00, 0x00, 0x00, 0xF0, 0x01, 0x00, 0xFC, 0x07, 0x00, 0xFE, 0x0F, 0x00, 0xFE, 0x0F, 0x08, 0x0F, 0x1E, 0x1C, 0x07, 0x1C, 0x1E, 0x07, 0x1C, 0x0F, 0x07, 0x9C, 0x0F, 0x07, 0xDC, 0x07, 0x0F, 0xFE, 0x03, 0xFE, 0xFF, 0x01, 0xFC, 0xFF, 0x00, 0xF8, 0x3F, 0x00, 0xE0, 0x0F, 0x00, 0x00, 0x00, 0x00, // Code for char 9
0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x0C, 0xC0, 0x03, 0x1E, 0xC0, 0x03, 0x1E, 0x80, 0x01, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // Code for char :
};

1590
libraries/SondeLib/Display.cpp Executable file

File diff suppressed because it is too large Load Diff

162
libraries/SondeLib/Display.h Executable file
View File

@ -0,0 +1,162 @@
#ifndef Display_h
#define Display_h
#define FONT_LARGE 1
#define FONT_SMALL 0
#include <SPI.h>
//#include <TFT22_ILI9225.h>
#include "gfxfont.h"
#include <U8x8lib.h>
#include <SPIFFS.h>
#define WIDTH_AUTO 9999
struct DispEntry {
int16_t y;
int16_t x;
int16_t fmt, width;
uint16_t fg,bg;
void (*func)(DispEntry *de);
const char *extra;
};
#define GPSUSE_BASE 1
#define GPSUSE_DIST 2
#define GPSUSE_BEARING 4
struct DispInfo {
DispEntry *de;
uint8_t *actions;
int16_t *timeouts;
const char *label;
uint8_t usegps;
};
struct StatInfo {
uint8_t len;
uint8_t size;
};
// Now starting towards supporting different Display types / libraries
class RawDisplay {
public:
virtual void begin() = 0;
virtual void clear() = 0;
virtual void setFont(uint8_t fontindex) = 0;
virtual void getDispSize(uint8_t *height, uint8_t *width, uint8_t *lineskip, uint8_t *colskip) = 0;
virtual void drawString(uint8_t x, uint8_t y, const char *s, int16_t width=WIDTH_AUTO, uint16_t fg=0xffff, uint16_t bg=0 ) = 0;
virtual void drawTile(uint8_t x, uint8_t y, uint8_t cnt, uint8_t *tile_ptr) = 0;
virtual void drawTriangle(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t x3, uint16_t y3, uint16_t color, bool fill) = 0;
virtual void drawBitmap(uint16_t x1, uint16_t y1, const uint16_t* bitmap, int16_t w, int16_t h) = 0;
virtual void welcome() = 0;
virtual void drawIP(uint8_t x, uint8_t y, int16_t width=WIDTH_AUTO, uint16_t fg=0xffff, uint16_t bg=0 ) = 0;
virtual void drawQS(uint8_t x, uint8_t y, uint8_t len, uint8_t size, uint8_t *stat, uint16_t fg=0xffff, uint16_t bg=0) = 0;
};
class U8x8Display : public RawDisplay {
private:
U8X8 *u8x8 = NULL; // initialize later after reading config file
int _type;
const uint8_t **fontlist;
int nfonts;
public:
U8x8Display(int type = 0) { _type = type; }
void begin();
void clear();
void setFont(uint8_t fontindex);
void getDispSize(uint8_t *height, uint8_t *width, uint8_t *lineskip, uint8_t *colskip);
void drawString(uint8_t x, uint8_t y, const char *s, int16_t width=WIDTH_AUTO, uint16_t fg=0xffff, uint16_t bg=0);
void drawTile(uint8_t x, uint8_t y, uint8_t cnt, uint8_t *tile_ptr);
void drawTriangle(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t x3, uint16_t y3, uint16_t color, bool fill);
void drawBitmap(uint16_t x1, uint16_t y1, const uint16_t* bitmap, int16_t w, int16_t h);
void welcome();
void drawIP(uint8_t x, uint8_t y, int16_t width=WIDTH_AUTO, uint16_t fg=0xffff, uint16_t bg=0);
void drawQS(uint8_t x, uint8_t y, uint8_t len, uint8_t size, uint8_t *stat, uint16_t fg=0xffff, uint16_t bg=0);
};
class Display {
private:
void replaceLayouts(DispInfo *newlayout, int nnew);
int allocDispInfo(int entries, DispInfo *d, char *label);
void parseDispElement(char *text, DispEntry *de);
int xscale=13, yscale=22;
int fontsma=0, fontlar=1;
uint16_t colfg, colbg;
static void circ(int x, int y);
static int countEntries(File f);
void calcGPS();
boolean gpsValid;
float gpsLat, gpsLon;
int gpsAlt;
int gpsDist; // -1: invalid
int gpsCourse, gpsDir, gpsBear; // 0..360; -1: invalid
boolean gpsCourseOld;
static const int LINEBUFLEN{ 255 };
static char lineBuf[LINEBUFLEN];
static const char *trim(char *s) {
char *ret = s;
while(*ret && isspace(*ret)) { ret++; }
int lastidx;
while(1) {
lastidx = strlen(ret)-1;
if(lastidx>=0 && isspace(ret[lastidx]))
ret[lastidx] = 0;
else
break;
}
return ret;
}
public:
void initFromFile();
int layoutIdx;
DispInfo *layout;
DispInfo *layouts;
int nLayouts;
static RawDisplay *rdis;
Display();
void init();
static char buf[17];
static void drawLat(DispEntry *de);
static void drawLon(DispEntry *de);
static void drawAz(DispEntry *de);
static void drawVBat(DispEntry *de);
static void drawAlt(DispEntry *de);
static void drawHS(DispEntry *de);
static void drawVS(DispEntry *de);
static void drawID(DispEntry *de);
static void drawRSSI(DispEntry *de);
static void drawQS(DispEntry *de);
static void drawType(DispEntry *de);
static void drawFreq(DispEntry *de);
static void drawAFC(DispEntry *de);
static void drawIP(DispEntry *de);
static void drawSite(DispEntry *de);
static void drawTelemetry(DispEntry *de);
static void drawKilltimer(DispEntry *de);
static void drawGPS(DispEntry *de);
static void drawText(DispEntry *de);
static void drawBatt(DispEntry *de);
static void drawString(DispEntry *de, const char *str);
void clearIP();
void setIP(const char *ip, bool AP);
void updateDisplayPos();
void updateDisplayPos2();
void updateDisplayAz();
void updateDisplayVBat();
void updateDisplayID();
void updateDisplayRSSI();
void updateStat();
void updateDisplayRXConfig();
void updateDisplayIP();
void updateDisplay();
void updateDisplayVBatt();
void setLayout(int layout);
};
extern Display disp;
#endif

529
libraries/SondeLib/M10.cpp Executable file
View File

@ -0,0 +1,529 @@
/* M10 decoder functions */
#include "M10.h"
#include "SX1278FSK.h"
#include "rsc.h"
#include "Sonde.h"
#include <SPIFFS.h>
// well...
//#include "rs92gps.h"
#define M10_DEBUG 1
#if M10_DEBUG
#define M10_DBG(x) x
#else
#define M10_DBG(x)
#endif
static byte data1[512];
static byte *dataptr=data1;
static uint8_t rxbitc;
static uint16_t rxbyte;
static int rxp=0;
static int haveNewFrame = 0;
static int lastFrame = 0;
static int headerDetected = 0;
int M10::setup(float frequency)
{
#if M10_DEBUG
Serial.println("Setup sx1278 for M10 sonde");
#endif
//if(!initialized) {
//Gencrctab();
//initrsc();
// not here for now.... get_eph("/brdc.19n");
// initialized = true;
//}
if(sx1278.ON()!=0) {
M10_DBG(Serial.println("Setting SX1278 power on FAILED"));
return 1;
}
if(sx1278.setFSK()!=0) {
M10_DBG(Serial.println("Setting FSJ mode FAILED"));
return 1;
}
if(sx1278.setBitrate(9600)!=0) {
M10_DBG(Serial.println("Setting bitrate 9600bit/s FAILED"));
return 1;
}
#if M10_DEBUG
float br = sx1278.getBitrate();
Serial.print("Exact bitrate is ");
Serial.println(br);
#endif
if(sx1278.setAFCBandwidth(sonde.config.rs92.rxbw)!=0) {
M10_DBG(Serial.printf("Setting AFC bandwidth %d Hz FAILED", sonde.config.rs92.rxbw));
return 1;
}
if(sx1278.setRxBandwidth(sonde.config.rs92.rxbw)!=0) {
M10_DBG(Serial.printf("Setting RX bandwidth to %d Hz FAILED", sonde.config.rs92.rxbw));
return 1;
}
// Enable auto-AFC, auto-AGC, RX Trigger by preamble
if(sx1278.setRxConf(0x1E)!=0) {
M10_DBG(Serial.println("Setting RX Config FAILED"));
return 1;
}
// Set autostart_RX to 01, preamble 0, SYNC detect==on, syncsize=3 (==4 byte
//char header[] = "0110.0101 0110.0110 1010.0101 1010.1010";
//const char *SYNC="\x10\xB6\xCA\x11\x22\x96\x12\xF8";
//const char *SYNC="\x08\x6D\x53\x88\x44\x69\x48\x1F";
// was 0x57
//const char *SYNC="\x99\x9A";
#if 1
// version 1, working with continuous RX
const char *SYNC="\x66\x65";
if(sx1278.setSyncConf(0x70, 2, (const uint8_t *)SYNC)!=0) {
M10_DBG(Serial.println("Setting SYNC Config FAILED"));
return 1;
}
//if(sx1278.setPreambleDetect(0xA8)!=0) {
if(sx1278.setPreambleDetect(0x9F)!=0) {
M10_DBG(Serial.println("Setting PreambleDetect FAILED"));
return 1;
}
#endif
#if 0
// version 2, with per-packet rx start, untested
// header is 2a 10 65, i.e. with lsb first
// 0 0101 0100 1 0 0000 1000 1 0 1010 0110 1
// 10 10011001 10011010 01 10 10101010 01101010 01 10 01100110 10010110 01
// preamble 0x6A 0x66 0x6A
// i.e. preamble detector on (0x80), preamble detector size 1 (0x00), preample chip errors??? (0x0A)
// after 2a2a2a2a2a1065
if(sx1278.setPreambleDetect(0xA8)!=0) {
M10_DBG(Serial.println("Setting PreambleDetect FAILED"));
return 1;
}
// sync config: ato restart (01), preamble polarity AA (0), sync on (1), resevered (0), syncsize 2+1 (010) => 0x52
const char *SYNC="\x6A\x66\x69";
if(sx1278.setSyncConf(0x52, 3, (const uint8_t *)SYNC)!=0) {
M10_DBG(Serial.println("Setting SYNC Config FAILED"));
return 1;
}
// payload length is ((240 - 7)*10 +6)/8 = 292
#endif
// Packet config 1: fixed len, no mancecer, no crc, no address filter
// Packet config 2: packet mode, no home ctrl, no beackn, msb(packetlen)=0)
if(sx1278.setPacketConfig(0x08, 0x40)!=0) {
M10_DBG(Serial.println("Setting Packet config FAILED"));
return 1;
}
Serial.print("M10: setting RX frequency to ");
Serial.println(frequency);
int res = sx1278.setFrequency(frequency);
// enable RX
sx1278.setPayloadLength(0); // infinite for now...
//sx1278.setPayloadLength(292);
sx1278.writeRegister(REG_OP_MODE, FSK_RX_MODE);
#if M10_DEBUG
M10_DBG(Serial.println("Setting SX1278 config for M10 finished\n"); Serial.println());
#endif
return res;
}
#if 0
int M10::setFrequency(float frequency) {
Serial.print("M10: setting RX frequency to ");
Serial.println(frequency);
int res = sx1278.setFrequency(frequency);
// enable RX
sx1278.setPayloadLength(0); // infinite for now...
sx1278.writeRegister(REG_OP_MODE, FSK_RX_MODE);
return res;
}
#endif
#if 0
uint32_t M10::bits2val(const uint8_t *bits, int len) {
uint32_t val = 0;
for (int j = 0; j < len; j++) {
val |= (bits[j] << (len-1-j));
}
return val;
}
#endif
M10::M10() {
}
#define M10_FRAMELEN 101
#define M10_CRCPOS 99
void M10::printRaw(uint8_t *data, int len)
{
char buf[3];
int i;
for(i=0; i<len; i++) {
snprintf(buf, 3, "%02X ", data[i]);
Serial.print(buf);
}
Serial.println();
}
static int update_checkM10(int c, uint8_t b) {
int c0, c1, t, t6, t7, s;
c1 = c & 0xFF;
// B
b = (b >> 1) | ((b & 1) << 7);
b ^= (b >> 2) & 0xFF;
// A1
t6 = ( c & 1) ^ ((c >> 2) & 1) ^ ((c >> 4) & 1);
t7 = ((c >> 1) & 1) ^ ((c >> 3) & 1) ^ ((c >> 5) & 1);
t = (c & 0x3F) | (t6 << 6) | (t7 << 7);
// A2
s = (c >> 7) & 0xFF;
s ^= (s >> 2) & 0xFF;
c0 = b ^ t ^ s;
return ((c1 << 8) | c0) & 0xFFFF;
}
static bool checkM10crc(uint8_t *msg) {
int i, cs, cs1;
cs = 0;
for (i = 0; i < M10_CRCPOS; i++) {
cs = update_checkM10(cs, msg[i]);
}
cs = cs & 0xFFFF;
cs1 = (msg[M10_CRCPOS] << 8) | msg[M10_CRCPOS+1];
return (cs1 == cs);
}
typedef uint32_t SET256[8];
static SET256 sondeudp_VARSET = {0x03BBBBF0UL,0x80600000UL,0x06A001A0UL,
0x0000001CUL,0x00000000UL,0x00000000UL,0x00000000UL,
0x00000000UL};
// VARSET=SET256{4..9,11..13,15..17,19..21,23..25,53..54,63,69,71,72,85,87,89,90,98..100};
static uint8_t fixcnt[M10_FRAMELEN];
static uint8_t fixbytes[M10_FRAMELEN];
static int32_t getint32(uint8_t *data) {
return (int32_t)( data[3]|(data[2]<<8)|(data[1]<<16)|(data[0]<<24) );
}
static int16_t getint16(uint8_t *data) {
return (int16_t)(data[1]|((uint16_t)data[0]<<8));
}
static char dez(uint8_t nr) {
nr = nr%10;
return '0'+nr;
}
static char hex(uint8_t nr) {
nr = nr&0x0f;
if(nr<10) return '0'+nr;
else return 'A'+nr-10;
}
const static float DEGMUL = 1.0/0xB60B60;
#define VMUL 0.005
#ifndef PI
#define PI (3.1415926535897932384626433832795)
#endif
#define RAD (PI/180)
// ret: 1=frame ok; 2=frame with errors; 0=ignored frame (m10dop-alternativ)
int M10::decodeframeM10(uint8_t *data) {
int repairstep = 16;
int repl = 0;
bool crcok;
// error correction, inspired by oe5dxl's sondeudp
do {
crcok = checkM10crc(data);
if(crcok) break;
repl = 0;
for(int i=0; i<M10_CRCPOS; i++) {
if( ((sondeudp_VARSET[i/32]&(1<<(i%32))) != 1) && (fixcnt[i]>=repairstep) ) {
repl++;
data[i] = fixbytes[i];
}
}
repairstep >>= 1;
} while(repairstep>0);
if(crcok) {
for(int i=0; i<M10_CRCPOS; i++) {
if(fixbytes[i]==data[i] &&fixcnt[i]<255) fixcnt[i]++;
else { fixcnt[i]=0; fixbytes[i]=data[i]; }
}
}
Serial.println(crcok?"CRC OK":"CRC NOT OK");
if(data[1]==0x9F && data[2]==0x20) {
Serial.println("Decoding...");
// Its a M10
// getid...
char ids[11];
ids[0] = 'M';
ids[1] = 'E';
ids[2] = hex(data[95]/16);
ids[3] = hex(data[95]);
ids[4] = hex(data[93]);
uint32_t id = data[96] + data[97]*256;
ids[5] = hex(id/4096);
ids[6] = hex(id/256);
ids[7] = hex(id/16);
ids[8] = hex(id);
ids[9] = 0;
strncpy(sonde.si()->id, ids, 10);
ids[0] = hex(data[95]/16);
ids[1] = dez((data[95]&0x0f)/10);
ids[2] = dez((data[95]&0x0f));
ids[3] = dez(data[93]);
ids[4] = dez(id>>13);
id &= 0x1fff;
ids[5] = dez(id/1000);
ids[6] = dez((id/100)%10);
ids[7] = dez((id/10)%10);
ids[8] = dez(id%10);
strncpy(sonde.si()->ser, ids, 10);
sonde.si()->validID = true;
Serial.printf("ID is %s [%02x %02x %d]\n", ids, data[95], data[93], id);
// ID printed on sonde is ...-.-abbbb, with a=id>>13, bbbb=id&0x1fff in decimal
// position data
sonde.si()->lat = getint32(data+14) * DEGMUL;
sonde.si()->lon = getint32(data+18) * DEGMUL;
sonde.si()->alt = getint32(data+22) * 0.001;
float ve = getint16(data+4)*VMUL;
float vn = getint16(data+6)*VMUL;
sonde.si()->vs = getint16(data+8) * VMUL;
sonde.si()->hs = sqrt(ve*ve+vn*vn);
float dir = atan2(vn, ve)*(1.0/RAD);
if(dir<0) dir+=360;
sonde.si()->dir = dir;
sonde.si()->validPos = 0x3f;
uint32_t gpstime = getint32(data+10);
uint16_t gpsweek = getint16(data+32);
// UTC is GPSTIME - 18s (24*60*60-18 = 86382)
// one week = 7*24*60*60 = 604800 seconds
// unix epoch starts jan 1st 1970 0:00
// gps time starts jan 6, 1980 0:00. thats 315964800 epoch seconds.
// subtracting 86400 yields 315878400UL
sonde.si()->time = (gpstime/1000) + 86382 + gpsweek*604800 + 315878400UL;
sonde.si()->validTime = true;
} else {
Serial.printf("data is %02x %02x %02x\n", data[0], data[1], data[2]);
return 0;
}
return crcok?1:2;
}
static uint32_t rxdata;
static bool rxsearching=true;
// search for
// //101001100110011010011010011001100110100110101010100110101001
// //1010011001100110100110100110 0110.0110 1001.1010 1010.1001 1010.1001 => 0x669AA9A9
void M10::processM10data(uint8_t dt)
{
for(int i=0; i<8; i++) {
uint8_t d = (dt&0x80)?1:0;
dt <<= 1;
rxdata = (rxdata<<1) | d;
//uint8_t value = ((rxdata>>1)^rxdata)&0x01;
//if((rxbitc&1)==1) { rxbyte = (rxbyte>>1) + ((value)<<8); } // mancester decoded data
//rxbyte = (rxbyte>>1) | (d<<8);
if( (rxbitc&1)==0 ) {
// "bit1"
rxbyte = (rxbyte<<1) | d;
} else {
// "bit2" ==> 01 or 10 => 1, otherweise => 0
rxbyte = rxbyte ^ d;
}
//
if(rxsearching) {
if( rxdata == 0xcccca64c || rxdata == 0x333359b3 ) {
rxsearching = false;
rxbitc = 0;
rxp = 0;
#if 1
int rssi=sx1278.getRSSI();
int fei=sx1278.getFEI();
int afc=sx1278.getAFC();
Serial.print("Test: RSSI="); Serial.print(rssi);
Serial.print(" FEI="); Serial.print(fei);
Serial.print(" AFC="); Serial.println(afc);
sonde.si()->rssi = rssi;
sonde.si()->afc = afc;
#endif
}
} else {
rxbitc = (rxbitc+1)%16; // 16;
if(rxbitc == 0) { // got 8 data bit
//Serial.printf("%03x ",rxbyte);
dataptr[rxp++] = rxbyte&0xff; // (rxbyte>>1)&0xff;
#if 0
if(rxp==7 && dataptr[6] != 0x65) {
Serial.printf("wrong start: %02x\n",dataptr[6]);
rxsearching = true;
}
#endif
if(rxp>=M10_FRAMELEN) {
rxsearching = true;
haveNewFrame = decodeframeM10(dataptr);
}
}
}
}
}
int M10::receive() {
unsigned long t0 = millis();
Serial.printf("M10::receive() start at %ld\n",t0);
while( millis() - t0 < 1000 ) {
uint8_t value = sx1278.readRegister(REG_IRQ_FLAGS2);
if ( bitRead(value, 7) ) {
Serial.println("FIFO full");
}
if ( bitRead(value, 4) ) {
Serial.println("FIFO overflow");
}
if ( bitRead(value, 2) == 1 ) {
Serial.println("FIFO: ready()");
sx1278.clearIRQFlags();
}
if(bitRead(value, 6) == 0) { // while FIFO not empty
byte data = sx1278.readRegister(REG_FIFO);
//Serial.printf("%02x",data);
processM10data(data);
value = sx1278.readRegister(REG_IRQ_FLAGS2);
} else {
if(headerDetected) {
t0 = millis(); // restart timer... don't time out if header detected...
headerDetected = 0;
}
if(haveNewFrame) {
Serial.printf("M10::receive(): new frame complete after %ldms\n", millis()-t0);
printRaw(dataptr, M10_FRAMELEN);
int retval = haveNewFrame==1 ? RX_OK: RX_ERROR;
haveNewFrame = 0;
return retval;
}
delay(2);
}
}
Serial.printf("M10::receive() timed out\n");
return RX_TIMEOUT; // TODO RX_OK;
}
#define M10MAXLEN (240)
int M10::waitRXcomplete() {
// called after complete...
#if 0
Serial.printf("decoding frame %d\n", lastFrame);
print_frame(lastFrame==1?data1:data2, 240);
SondeInfo *si = sonde.sondeList+rxtask.receiveSonde;
si->lat = gpx.lat;
si->lon = gpx.lon;
si->alt = gpx.alt;
si->vs = gpx.vU;
si->hs = gpx.vH;
si->dir = gpx.vD;
si->validPos = 0x3f;
memcpy(si->id, gpx.id, 9);
si->validID = true;
int res=0;
uint32_t t0 = millis();
while( rxtask.receiveResult == 0xFFFF && millis()-t0 < 2000) { delay(20); }
if( rxtask.receiveResult<0 || rxtask.receiveResult==RX_TIMEOUT) {
res = RX_TIMEOUT;
} else if ( rxtask.receiveResult==0) {
res = RX_OK;
} else {
res = RX_ERROR;
}
rxtask.receiveResult = 0xFFFF;
Serial.printf("M10::waitRXcomplete returning %d (%s)\n", res, RXstr[res]);
return res;
#endif
return 0;
}
#if 0
int oldwaitRXcomplete() {
Serial.println("M10: receive frame...\n");
sx1278receiveData = true;
delay(6000); // done in other task....
//sx1278receiveData = false;
#if 0
//sx1278.setPayloadLength(518-8); // Expect 320-8 bytes or 518-8 bytes (8 byte header)
//sx1278.setPayloadLength(0); // infinite for now...
////// test code for continuous reception
// sx1278.receive(); /// active FSK RX mode -- already done above...
uint8_t value = sx1278.readRegister(REG_IRQ_FLAGS2);
unsigned long previous = millis();
byte ready=0;
uint32_t wait = 8000;
// while not yet done or FIFO not yet empty
// bit 6: FIFO Empty
// bit 2 payload ready
int by=0;
while( (!ready || bitRead(value,6)==0) && (millis() - previous < wait) )
{
if( bitRead(value, 7) ) { Serial.println("FIFO full"); }
if( bitRead(value, 4) ) { Serial.println("FIFO overflow"); }
if( bitRead(value,2)==1 ) ready=1;
if( bitRead(value, 6) == 0 ) { // FIFO not empty
byte data = sx1278.readRegister(REG_FIFO);
process8N1data(data);
by++;
#if 0
if(di==1) {
int rssi=getRSSI();
int fei=getFEI();
int afc=getAFC();
Serial.print("Test: RSSI="); Serial.println(rssi);
Serial.print("Test: FEI="); Serial.println(fei);
Serial.print("Test: AFC="); Serial.println(afc);
sonde.si()->rssi = rssi;
sonde.si()->afc = afc;
}
if(di>520) {
// TODO
Serial.println("TOO MUCH DATA");
break;
}
previous = millis(); // reset timeout after receiving data
#endif
}
value = sx1278.readRegister(REG_IRQ_FLAGS2);
}
Serial.printf("processed %d bytes before end/timeout\n", by);
#endif
/////
#if 0
int e = sx1278.receivePacketTimeout(1000, data+8);
if(e) { Serial.println("TIMEOUT"); return RX_TIMEOUT; } //if timeout... return 1
printRaw(data, M10MAXLEN);
//for(int i=0; i<M10MAXLEN; i++) { data[i] = reverse(data[i]); }
//printRaw(data, MAXLEN);
//for(int i=0; i<M10MAXLEN; i++) { data[i] = data[i] ^ scramble[i&0x3F]; }
//printRaw(data, MAXLEN);
//int res = decode41(data, M10MAXLEN);
#endif
int res=0;
return res==0 ? RX_OK : RX_ERROR;
}
#endif
M10 m10 = M10();

96
libraries/SondeLib/M10.h Executable file
View File

@ -0,0 +1,96 @@
/*
* M10.h
* Functions for decoding Meteomodem M10 sondes with SX127x chips
* Copyright (C) 2019 Hansi Reiser, dl9rdz
*
* SPDX-License-Identifier: GPL-2.0+
*/
#ifndef M10_h
#define M10_h
#include <stdlib.h>
#include <stdint.h>
#include <Arduino.h>
#ifndef inttypes_h
#include <inttypes.h>
#endif
#if 0
struct CONTEXTR9 {
char calibdata[512];
uint32_t calibok;
char mesok;
char posok;
char framesent;
double lat;
double lon;
double heig;
double speed;
double dir;
double climb;
double lastlat;
double laslong;
double lastalt;
double lastspeed;
double lastdir;
double lastclb;
float hrmsc;
float vrmsc;
double hp;
double hyg;
double temp;
double ozontemp;
double ozon;
uint32_t goodsats;
uint32_t timems;
uint32_t framenum;
};
#endif
/* Main class */
class M10
{
private:
void printRaw(uint8_t *data, int len);
void processM10data(uint8_t data);
int decodeframeM10(uint8_t *data);
#if 0
void stobyte92(uint8_t byte);
void dogps(const uint8_t *sf, int sf_len,
struct CONTEXTR9 * cont, uint32_t * timems,
uint32_t * gpstime);
uint32_t bits2val(const uint8_t *bits, int len);
int bitsToBytes(uint8_t *bits, uint8_t *bytes, int len);
int decode92(byte *data, int maxlen);
uint8_t hamming_conf[ 7*8]; // 7*8=56
uint8_t hamming_dat1[13*8]; // 13*8=104
uint8_t hamming_dat2[13*8];
uint8_t block_conf[ 7*4]; // 7*4=28
uint8_t block_dat1[13*4]; // 13*4=52
uint8_t block_dat2[13*4];
uint8_t H[4][8] = // extended Hamming(8,4) particy check matrix
{{ 0, 1, 1, 1, 1, 0, 0, 0},
{ 1, 0, 1, 1, 0, 1, 0, 0},
{ 1, 1, 0, 1, 0, 0, 1, 0},
{ 1, 1, 1, 0, 0, 0, 0, 1}};
uint8_t He[8] = { 0x7, 0xB, 0xD, 0xE, 0x8, 0x4, 0x2, 0x1}; // Spalten von H:
// 1-bit-error-Syndrome
boolean initialized = false;
#endif
public:
M10();
int setup(float frequency);
int receive();
int waitRXcomplete();
//int use_ecc = 1;
};
extern M10 m10;
#endif

556
libraries/SondeLib/RS41.cpp Executable file
View File

@ -0,0 +1,556 @@
/* RS41 decoder functions */
#include "RS41.h"
#include "SX1278FSK.h"
#include "rsc.h"
#include "Sonde.h"
#define RS41_DEBUG 0
#if RS41_DEBUG
#define RS41_DBG(x) x
#else
#define RS41_DBG(x)
#endif
#define RS41MAXLEN (320)
static byte data[800];
static int dpos = 0;
static uint16_t CRCTAB[256];
#define X2C_DIVR(a, b) ((b) != 0.0f ? (a)/(b) : (a))
#define X2C_DIVL(a, b) ((a)/(b))
static uint32_t X2C_LSH(uint32_t a, int32_t length, int32_t n)
{
uint32_t m;
m = 0;
m = (length == 32) ? 0xFFFFFFFFl : (1 << length) - 1;
if (n > 0) {
if (n >= (int32_t)length)
return 0;
return (a << n) & m;
}
if (n <= (int32_t)-length)
return 0;
return (a >> -n) & m;
}
static double atang2(double x, double y)
{
double w;
if (fabs(x)>fabs(y)) {
w = (double)atan((float)(X2C_DIVL(y,x)));
if (x<0.0) {
if (y>0.0) w = 3.1415926535898+w;
else w = w-3.1415926535898;
}
}
else if (y!=0.0) {
w = (double)(1.5707963267949f-atan((float)(X2C_DIVL(x,
y))));
if (y<0.0) w = w-3.1415926535898;
}
else w = 0.0;
return w;
} /* end atang2() */
static void Gencrctab(void)
{
uint16_t j;
uint16_t i;
uint16_t crc;
for (i = 0U; i<=255U; i++) {
crc = (uint16_t)(i*256U);
for (j = 0U; j<=7U; j++) {
if ((0x8000U & crc)) crc = X2C_LSH(crc,16,1)^0x1021U;
else crc = X2C_LSH(crc,16,1);
} /* end for */
CRCTAB[i] = X2C_LSH(crc,16,-8)|X2C_LSH(crc,16,8);
} /* end for */
} /* end Gencrctab() */
int RS41::setup(float frequency)
{
#if RS41_DEBUG
Serial.println("Setup sx1278 for RS41 sonde");
#endif
if(!initialized) {
Gencrctab();
initrsc();
initialized = true;
}
if(sx1278.ON()!=0) {
RS41_DBG(Serial.println("Setting SX1278 power on FAILED"));
return 1;
}
if(sx1278.setFSK()!=0) {
RS41_DBG(Serial.println("Setting FSK mode FAILED"));
return 1;
}
if(sx1278.setBitrate(4800)!=0) {
RS41_DBG(Serial.println("Setting bitrate 4800bit/s FAILED"));
return 1;
}
#if RS41_DEBUG
float br = sx1278.getBitrate();
Serial.print("Exact bitrate is ");
Serial.println(br);
#endif
if(sx1278.setAFCBandwidth(sonde.config.rs41.agcbw)!=0) {
RS41_DBG(Serial.printf("Setting AFC bandwidth %d Hz FAILED", sonde.config.rs41.agcbw));
return 1;
}
if(sx1278.setRxBandwidth(sonde.config.rs41.rxbw)!=0) {
RS41_DBG(Serial.printf("Setting RX bandwidth to %d Hz FAILED", sonde.config.rs41.rxbw));
return 1;
}
// Enable auto-AFC, auto-AGC, RX Trigger by preamble
if(sx1278.setRxConf(0x1E)!=0) {
RS41_DBG(Serial.println("Setting RX Config FAILED"));
return 1;
}
// Set autostart_RX to 01, preamble 0, SYNC detect==on, syncsize=3 (==4 byte
//char header[] = "0110.0101 0110.0110 1010.0101 1010.1010";
//const char *SYNC="\x10\xB6\xCA\x11\x22\x96\x12\xF8";
const char *SYNC="\x08\x6D\x53\x88\x44\x69\x48\x1F";
if(sx1278.setSyncConf(0x57, 8, (const uint8_t *)SYNC)!=0) {
RS41_DBG(Serial.println("Setting SYNC Config FAILED"));
return 1;
}
if(sx1278.setPreambleDetect(0xA8)!=0) {
RS41_DBG(Serial.println("Setting PreambleDetect FAILED"));
return 1;
}
// Packet config 1: fixed len, no mancecer, no crc, no address filter
// Packet config 2: packet mode, no home ctrl, no beackn, msb(packetlen)=0)
if(sx1278.setPacketConfig(0x08, 0x40)!=0) {
RS41_DBG(Serial.println("Setting Packet config FAILED"));
return 1;
}
Serial.print("RS41: setting RX frequency to ");
Serial.println(frequency);
int retval = sx1278.setFrequency(frequency);
dpos = 0;
#if RS41_DEBUG
RS41_DBG(Serial.println("Setting SX1278 config for RS41 finished\n"); Serial.println());
#endif
sx1278.clearIRQFlags();
// the following is already done in receivePacketTimeout()
// sx1278.setPayloadLength(RS41MAXLEN-8); // Expect 320-8 bytes or 518-8 bytes (8 byte header)
// sx1278.writeRegister(REG_OP_MODE, FSK_RX_MODE);
return retval;
}
uint32_t RS41::bits2val(const uint8_t *bits, int len) {
uint32_t val = 0;
for (int j = 0; j < len; j++) {
val |= (bits[j] << (len-1-j));
}
return val;
}
RS41::RS41() {
}
/* RS41 reed solomon decoder, from dxlAPRS
*/
static int32_t reedsolomon41(byte buf[], uint32_t buf_len, uint32_t len2)
{
uint32_t i;
int32_t res1;
/*tb1, */
int32_t res;
char b1[256];
char b[256];
uint32_t eraspos[24];
uint32_t tmp;
for (i = 0UL; i<=255UL; i++) {
b[i] = 0;
b1[i] = 0;
} /* end for */
tmp = len2;
i = 0UL;
if (i<=tmp) for (;; i++) {
b[230UL-i] = buf[i*2UL+56UL];
b1[230UL-i] = buf[i*2UL+57UL];
if (i==tmp) break;
} /* end for */
for (i = 0UL; i<=23UL; i++) {
b[254UL-i] = buf[i+8UL];
b1[254UL-i] = buf[i+32UL];
} /* end for */
res = decodersc(b, eraspos, 0);
res1 = decodersc(b1, eraspos, 0);
if (res>0L && res<=12L) {
tmp = len2;
i = 0UL;
if (i<=tmp) for (;; i++) {
buf[i*2UL+56UL] = b[230UL-i];
if (i==tmp) break;
} /* end for */
for (i = 0UL; i<=23UL; i++) {
buf[i+8UL] = b[254UL-i];
} /* end for */
}
if (res1>0L && res1<=12L) {
tmp = len2;
i = 0UL;
if (i<=tmp) for (;; i++) {
buf[i*2UL+57UL] = b1[230UL-i];
if (i==tmp) break;
} /* end for */
for (i = 0UL; i<=23UL; i++) {
buf[i+32UL] = b1[254UL-i];
} /* end for */
}
if (res<0L || res1<0L) return -1L;
else return res+res1;
return 0;
} /* end reedsolomon41() */
static char crcrs(const byte frame[], uint32_t frame_len,
int32_t from, int32_t to)
{
uint16_t crc;
int32_t i;
int32_t tmp;
crc = 0xFFFFU;
tmp = to-3L;
i = from;
if (i<=tmp) for (;; i++) {
crc = X2C_LSH(crc,16,-8)^CRCTAB[(uint32_t)((crc^(uint16_t)(uint8_t)frame[i])&0xFFU)];
if (i==tmp) break;
} /* end for */
return frame[to-1L]==(char)crc && frame[to-2L]==(char)X2C_LSH(crc,
16,-8);
} /* end crcrs() */
static int32_t getint32(const byte frame[], uint32_t frame_len,
uint32_t p)
{
uint32_t n;
uint32_t i;
n = 0UL;
for (i = 3UL;; i--) {
n = n*256UL+(uint32_t)(uint8_t)frame[p+i];
if (i==0UL) break;
} /* end for */
return (int32_t)n;
} /* end getint32() */
static uint32_t getcard16(const byte frame[], uint32_t frame_len,
uint32_t p)
{
return (uint32_t)(uint8_t)frame[p]+256UL*(uint32_t)(uint8_t)
frame[p+1UL];
} /* end getcard16() */
static int32_t getint16(const byte frame[], uint32_t frame_len,
uint32_t p)
{
uint32_t n;
n = (uint32_t)(uint8_t)frame[p]+256UL*(uint32_t)(uint8_t)
frame[p+1UL];
if (n>=32768UL) return (int32_t)(n-65536UL);
return (int32_t)n;
} /* end getint16() */
static void wgs84r(double x, double y, double z,
double * lat, double * long0,
double * heig)
{
double sl;
double ct;
double st;
double t;
double rh;
double xh;
double h;
h = x*x+y*y;
if (h>0.0) {
rh = (double)sqrt((float)h);
xh = x+rh;
*long0 = atang2(xh, y)*2.0;
if (*long0>3.1415926535898) *long0 = *long0-6.2831853071796;
t = (double)atan((float)(X2C_DIVL(z*1.003364089821,
rh)));
st = (double)sin((float)t);
ct = (double)cos((float)t);
*lat = (double)atan((float)
(X2C_DIVL(z+4.2841311513312E+4*st*st*st,
rh-4.269767270718E+4*ct*ct*ct)));
sl = (double)sin((float)*lat);
*heig = X2C_DIVL(rh,(double)cos((float)*lat))-(double)(X2C_DIVR(6.378137E+6f,
sqrt((float)(1.0-6.6943799901413E-3*sl*sl))));
}
else {
*lat = 0.0;
*long0 = 0.0;
*heig = 0.0;
}
/* lat:=atan(z/(rh*(1.0 - E2))); */
/* heig:=sqrt(h + z*z) - EARTHA; */
} /* end wgs84r() */
// returns: 0=ok, -1=error
static void posrs41(const byte b[], uint32_t b_len, uint32_t p)
{
double dir;
double vu;
double ve;
double vn;
double vz;
double vy;
double vx;
double heig;
double long0;
double lat;
double z;
double y;
double x;
x = (double)getint32(b, b_len, p)*0.01;
y = (double)getint32(b, b_len, p+4UL)*0.01;
z = (double)getint32(b, b_len, p+8UL)*0.01;
wgs84r(x, y, z, &lat, &long0, &heig);
Serial.print(" ");
sonde.si()->lat = (float)(X2C_DIVL(lat,1.7453292519943E-2));
Serial.print(sonde.si()->lat);
Serial.print(" ");
sonde.si()->lon = (float)(X2C_DIVL(long0,1.7453292519943E-2));
Serial.print(sonde.si()->lon);
if (heig<1.E+5 && heig>(-1.E+5)) {
Serial.print(" ");
Serial.print((uint32_t)heig);
Serial.print("m");
}
/*speed */
vx = (double)getint16(b, b_len, p+12UL)*0.01;
vy = (double)getint16(b, b_len, p+14UL)*0.01;
vz = (double)getint16(b, b_len, p+16UL)*0.01;
vn = (-(vx*(double)sin((float)lat)*(double)
cos((float)long0))-vy*(double)
sin((float)lat)*(double)sin((float)
long0))+vz*(double)cos((float)lat);
ve = -(vx*(double)sin((float)long0))+vy*(double)
cos((float)long0);
vu = vx*(double)cos((float)lat)*(double)
cos((float)long0)+vy*(double)
cos((float)lat)*(double)sin((float)
long0)+vz*(double)sin((float)lat);
dir = X2C_DIVL(atang2(vn, ve),1.7453292519943E-2);
if (dir<0.0) dir = 360.0+dir;
sonde.si()->dir = dir;
Serial.print(" ");
sonde.si()->hs = sqrt((float)(vn*vn+ve*ve))*3.6f;
Serial.print(sonde.si()->hs);
Serial.print("km/h ");
Serial.print(dir);
Serial.print("deg ");
Serial.print((float)vu);
sonde.si()->vs = vu;
Serial.print("m/s ");
uint8_t sats = getcard16(b, b_len, p+18UL)&255UL;
Serial.print(sats);
Serial.print("Sats");
sonde.si()->sats = sats;
sonde.si()->alt = heig;
if( 0==(int)(lat*10000) && 0==(int)(long0*10000) ) {
if(sonde.si()->validPos) {
// we have an old position, so keep previous position and mark it as old
sonde.si()->validPos |= 0x80;
}
}
else
sonde.si()->validPos = 0x7f;
} /* end posrs41() */
// returns: 0: ok, -1: rs or crc error
int RS41::decode41(byte *data, int maxlen)
{
char buf[128];
int crcok = 0;
int32_t corr = reedsolomon41(data, 560, 131); // try short frame first
if(corr<0) {
corr = reedsolomon41(data, 560, 230); // try long frame
}
#if 0
Serial.print("RS result:");
Serial.print(corr);
Serial.println();
#endif
int p = 57; // 8 byte header, 48 byte RS
while(p<maxlen) { /* why 555? */
uint8_t typ = data[p++];
uint32_t len = data[p++]+2UL;
if(p+len>maxlen) break;
#if 0
// DEBUG OUTPUT
Serial.print("@");
Serial.print(p-2);
Serial.print(": ID:");
Serial.print(typ, HEX);
Serial.print(", len=");
Serial.print(len);
Serial.print(": ");
for(int i=0; i<len-1; i++) {
char buf[3];
snprintf(buf, 4, "%02X|", data[p+i]);
Serial.print(buf);
}
#endif
// check CRC
if(!crcrs(data, 560, p, p+len)) {
Serial.println("###CRC ERROR###");
} else {
crcok = 1;
switch(typ) {
case 'y': // name
{
Serial.print("#");
uint16_t fnr = data[p]+(data[p+1]<<8);
Serial.print(fnr);
sonde.si()->frame = fnr;
Serial.print("; RS41 ID ");
snprintf(buf, 10, "%.8s ", data+p+2);
Serial.print(buf);
sonde.si()->type=STYPE_RS41;
strncpy(sonde.si()->id, (const char *)(data+p+2), 8);
sonde.si()->id[8]=0;
strncpy(sonde.si()->ser, (const char *)(data+p+2), 8);
sonde.si()->ser[8]=0;
sonde.si()->validID=true;
int calnr = data[p+23];
// not sure about this
if(calnr==0x31) {
uint16_t bt = data[p+30] + 256*data[p+31];
sonde.si()->burstKT = bt;
}
// this should be right...
if(calnr==0x02) {
uint16_t kt = data[p+31] + 256*data[p+32];
sonde.si()->launchKT = kt;
}
// and this seems fine as well...
if(calnr==0x32) {
uint16_t cntdown = data[p+24] + (data[p+25]<<8);
uint16_t min = cntdown - (cntdown/3600)*3600;
Serial.printf("Countdown value: %d\n [%2d:%02d:%02d]", cntdown, cntdown/3600, min/60, min-(min/60)*60);
sonde.si()->countKT = cntdown;
sonde.si()->crefKT = fnr;
}
}
// TODO: some more data
break;
case '|': // date
{
uint32_t gpstime = getint32(data, 560, p+2);
uint16_t gpsweek = getint16(data, 560, p);
// UTC is GPSTIME - 18s (24*60*60-18 = 86382)
// one week = 7*24*60*60 = 604800 seconds
// unix epoch starts jan 1st 1970 0:00
// gps time starts jan 6, 1980 0:00. thats 315964800 epoch seconds.
// subtracting 86400 yields 315878400UL
sonde.si()->time = (gpstime/1000) + 86382 + gpsweek*604800 + 315878400UL;
sonde.si()->validTime = true;
}
break;
case '{': // pos
posrs41(data+p, len, 0);
break;
default:
break;
}}
p += len;
Serial.println();
}
return crcok ? 0 : -1;
}
void RS41::printRaw(uint8_t *data, int len)
{
char buf[3];
int i;
for(i=0; i<len; i++) {
snprintf(buf, 3, "%02X", data[i]);
Serial.print(buf);
}
Serial.println();
}
void RS41::bitsToBytes(uint8_t *bits, uint8_t *bytes, int len)
{
int i;
for(i=0; i<len*4; i++) {
bytes[i/8] = (bytes[i/8]<<1) | (bits[i]?1:0);
}
bytes[(i-1)/8] &= 0x0F;
}
static unsigned char lookup[16] = {
0x0, 0x8, 0x4, 0xc, 0x2, 0xa, 0x6, 0xe,
0x1, 0x9, 0x5, 0xd, 0x3, 0xb, 0x7, 0xf, };
static uint8_t reverse(uint8_t n) {
return (lookup[n&0x0f] << 4) | lookup[n>>4];
}
static uint8_t scramble[64] = {150U,131U,62U,81U,177U,73U,8U,152U,50U,5U,89U,
14U,249U,68U,198U,38U,33U,96U,194U,234U,121U,93U,109U,161U,
84U,105U,71U,12U,220U,232U,92U,241U,247U,118U,130U,127U,7U,
153U,162U,44U,147U,124U,48U,99U,245U,16U,46U,97U,208U,188U,
180U,182U,6U,170U,244U,35U,120U,110U,59U,174U,191U,123U,76U,
193U};
int RS41::receive() {
sx1278.setPayloadLength(RS41MAXLEN-8);
int e = sx1278.receivePacketTimeout(1000, data+8);
if(e) { Serial.println("TIMEOUT"); return RX_TIMEOUT; }
for(int i=0; i<RS41MAXLEN; i++) { data[i] = reverse(data[i]); }
for(int i=0; i<RS41MAXLEN; i++) { data[i] = data[i] ^ scramble[i&0x3F]; }
return decode41(data, RS41MAXLEN);
}
int RS41::waitRXcomplete() {
// Currently not used. can be used for additinoal post-processing
// (required for RS92 to avoid FIFO overrun in rx task)
#if 0
int res;
uint32_t t0 = millis();
while(rxtask.receiveResult<0 && millis()-t0 < 3000) { delay(50); }
if(rxtask.receiveResult<0 || rxtask.receiveResult==RX_TIMEOUT) {
res = RX_TIMEOUT;
} else if (rxtask.receiveResult==0) {
res = RX_OK;
} else {
res = RX_ERROR;
}
rxtask.receiveResult = -1;
Serial.printf("waitRXcomplete returning %d\n", res);
return res;
#endif
return 0;
}
RS41 rs41 = RS41();

67
libraries/SondeLib/RS41.h Executable file
View File

@ -0,0 +1,67 @@
/*
* RS41.h
* Functions for decoding RS41 sondes with SX127x chips
* Copyright (C) 2019 Hansi Reiser, dl9rdz
*
* SPDX-License-Identifier: GPL-2.0+
*/
#ifndef RS41_h
#define RS41_h
#include <stdlib.h>
#include <stdint.h>
#include <Arduino.h>
#ifndef inttypes_h
#include <inttypes.h>
#endif
/* Main class */
class RS41
{
private:
uint32_t bits2val(const uint8_t *bits, int len);
void printRaw(uint8_t *data, int len);
void bitsToBytes(uint8_t *bits, uint8_t *bytes, int len);
int decode41(byte *data, int maxlen);
#define B 8
#define S 4
uint8_t hamming_conf[ 7*B]; // 7*8=56
uint8_t hamming_dat1[13*B]; // 13*8=104
uint8_t hamming_dat2[13*B];
uint8_t block_conf[ 7*S]; // 7*4=28
uint8_t block_dat1[13*S]; // 13*4=52
uint8_t block_dat2[13*S];
uint8_t H[4][8] = // extended Hamming(8,4) particy check matrix
{{ 0, 1, 1, 1, 1, 0, 0, 0},
{ 1, 0, 1, 1, 0, 1, 0, 0},
{ 1, 1, 0, 1, 0, 0, 1, 0},
{ 1, 1, 1, 0, 0, 0, 0, 1}};
uint8_t He[8] = { 0x7, 0xB, 0xD, 0xE, 0x8, 0x4, 0x2, 0x1}; // Spalten von H:
// 1-bit-error-Syndrome
boolean initialized = false;
public:
RS41();
// New interface:
// setup() is called when channel is activated (sets mode and frequency and activates receiver)
int setup(float frequency);
// processRXbyte is called by background task for each received byte
// should be fast enough to not cause sx127x fifo buffer overflow
// void processRXbyte(uint8_t data);
// is called approx. 1x per second, may do some post-processing of received data
// and update information in sonde data structure
// returns infomration about sucess/error (for timers and for quality bar in display)
int receive();
int waitRXcomplete();
//int receiveFrame();
int use_ecc = 1;
};
extern RS41 rs41;
#endif

727
libraries/SondeLib/RS92.cpp Executable file
View File

@ -0,0 +1,727 @@
/* RS92 decoder functions */
#include "RS92.h"
#include "SX1278FSK.h"
#include "rsc.h"
#include "Sonde.h"
#include <SPIFFS.h>
// well...
#include "rs92gps.h"
#define RS92_DEBUG 1
#if RS92_DEBUG
#define RS92_DBG(x) x
#else
#define RS92_DBG(x)
#endif
//static uint16_t CRCTAB[256];
uint16_t *CRCTAB = NULL;
#define X2C_DIVR(a, b) ((b) != 0.0f ? (a)/(b) : (a))
#define X2C_DIVL(a, b) ((a)/(b))
static uint32_t X2C_LSH(uint32_t a, int32_t length, int32_t n)
{
uint32_t m;
m = 0;
m = (length == 32) ? 0xFFFFFFFFl : (1 << length) - 1;
if (n > 0) {
if (n >= (int32_t)length)
return 0;
return (a << n) & m;
}
if (n <= (int32_t)-length)
return 0;
return (a >> -n) & m;
}
#if 0
static double atang2(double x, double y)
{
double w;
if (fabs(x)>fabs(y)) {
w = (double)atan((float)(X2C_DIVL(y,x)));
if (x<0.0) {
if (y>0.0) w = 3.1415926535898+w;
else w = w-3.1415926535898;
}
}
else if (y!=0.0) {
w = (double)(1.5707963267949f-atan((float)(X2C_DIVL(x,
y))));
if (y<0.0) w = w-3.1415926535898;
}
else w = 0.0;
return w;
} /* end atang2() */
#endif
static void Gencrctab(void)
{
uint16_t j;
uint16_t i;
uint16_t crc;
if(!CRCTAB) { CRCTAB=(uint16_t *)malloc(256*sizeof(uint16_t)); }
for (i = 0U; i<=255U; i++) {
crc = (uint16_t)(i*256U);
for (j = 0U; j<=7U; j++) {
if ((0x8000U & crc)) crc = X2C_LSH(crc,16,1)^0x1021U;
else crc = X2C_LSH(crc,16,1);
} /* end for */
CRCTAB[i] = X2C_LSH(crc,16,-8)|X2C_LSH(crc,16,8);
} /* end for */
} /* end Gencrctab() */
static byte data1[512]={0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x10};
static byte data2[512]={0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x10};
static byte *dataptr=data1;
static uint8_t rxbitc;
static int32_t asynst[10]={0};
static uint16_t rxbyte;
int rxp=0;
static int haveNewFrame = 0;
static int lastFrame = 0;
static int headerDetected = 0;
int RS92::setup(float frequency)
{
#if RS92_DEBUG
Serial.println("Setup sx1278 for RS92 sonde");
#endif
if(!initialized) {
Gencrctab();
initrsc();
// not here for now.... get_eph("/brdc.19n");
initialized = true;
}
if(sx1278.ON()!=0) {
RS92_DBG(Serial.println("Setting SX1278 power on FAILED"));
return 1;
}
if(sx1278.setFSK()!=0) {
RS92_DBG(Serial.println("Setting FSJ mode FAILED"));
return 1;
}
if(sx1278.setBitrate(4800)!=0) {
RS92_DBG(Serial.println("Setting bitrate 4800bit/s FAILED"));
return 1;
}
#if RS92_DEBUG
float br = sx1278.getBitrate();
Serial.print("Exact bitrate is ");
Serial.println(br);
#endif
if(sx1278.setAFCBandwidth(sonde.config.rs92.rxbw)!=0) {
RS92_DBG(Serial.printf("Setting AFC bandwidth %d Hz FAILED", sonde.config.rs92.rxbw));
return 1;
}
if(sx1278.setRxBandwidth(sonde.config.rs92.rxbw)!=0) {
RS92_DBG(Serial.printf("Setting RX bandwidth to %d Hz FAILED", sonde.config.rs92.rxbw));
return 1;
}
// Enable auto-AFC, auto-AGC, RX Trigger by preamble
if(sx1278.setRxConf(0x1E)!=0) {
RS92_DBG(Serial.println("Setting RX Config FAILED"));
return 1;
}
// Set autostart_RX to 01, preamble 0, SYNC detect==on, syncsize=3 (==4 byte
//char header[] = "0110.0101 0110.0110 1010.0101 1010.1010";
//const char *SYNC="\x10\xB6\xCA\x11\x22\x96\x12\xF8";
//const char *SYNC="\x08\x6D\x53\x88\x44\x69\x48\x1F";
// was 0x57
//const char *SYNC="\x99\x9A";
#if 1
// version 1, working with continuous RX
const char *SYNC="\x66\x65";
if(sx1278.setSyncConf(0x70, 2, (const uint8_t *)SYNC)!=0) {
RS92_DBG(Serial.println("Setting SYNC Config FAILED"));
return 1;
}
if(sx1278.setPreambleDetect(0xA8)!=0) {
RS92_DBG(Serial.println("Setting PreambleDetect FAILED"));
return 1;
}
#endif
#if 0
// version 2, with per-packet rx start, untested
// header is 2a 10 65, i.e. with lsb first
// 0 0101 0100 1 0 0000 1000 1 0 1010 0110 1
// 10 10011001 10011010 01 10 10101010 01101010 01 10 01100110 10010110 01
// preamble 0x6A 0x66 0x6A
// i.e. preamble detector on (0x80), preamble detector size 1 (0x00), preample chip errors??? (0x0A)
// after 2a2a2a2a2a1065
if(sx1278.setPreambleDetect(0xA8)!=0) {
RS92_DBG(Serial.println("Setting PreambleDetect FAILED"));
return 1;
}
// sync config: ato restart (01), preamble polarity AA (0), sync on (1), resevered (0), syncsize 2+1 (010) => 0x52
const char *SYNC="\x6A\x66\x69";
if(sx1278.setSyncConf(0x52, 3, (const uint8_t *)SYNC)!=0) {
RS92_DBG(Serial.println("Setting SYNC Config FAILED"));
return 1;
}
// payload length is ((240 - 7)*10 +6)/8 = 292
#endif
// Packet config 1: fixed len, no mancecer, no crc, no address filter
// Packet config 2: packet mode, no home ctrl, no beackn, msb(packetlen)=0)
if(sx1278.setPacketConfig(0x08, 0x40)!=0) {
RS92_DBG(Serial.println("Setting Packet config FAILED"));
return 1;
}
Serial.print("RS92: setting RX frequency to ");
Serial.println(frequency);
int res = sx1278.setFrequency(frequency);
sx1278.clearIRQFlags();
// enable RX
sx1278.setPayloadLength(0); // infinite for now...
//sx1278.setPayloadLength(292);
sx1278.writeRegister(REG_OP_MODE, FSK_RX_MODE);
#if RS92_DEBUG
RS92_DBG(Serial.println("Setting SX1278 config for RS92 finished\n"); Serial.println());
#endif
return res;
}
#if 0
int RS92::setFrequency(float frequency) {
Serial.print("RS92: setting RX frequency to ");
Serial.println(frequency);
int res = sx1278.setFrequency(frequency);
// enable RX
sx1278.setPayloadLength(0); // infinite for now...
sx1278.writeRegister(REG_OP_MODE, FSK_RX_MODE);
return res;
}
#endif
uint32_t RS92::bits2val(const uint8_t *bits, int len) {
uint32_t val = 0;
for (int j = 0; j < len; j++) {
val |= (bits[j] << (len-1-j));
}
return val;
}
RS92::RS92() {
}
/* RS92 reed solomon decoder, from dxlAPRS
*/
#if 0
static char crcrs(const byte frame[], uint32_t frame_len,
int32_t from, int32_t to)
{
uint16_t crc;
int32_t i;
int32_t tmp;
crc = 0xFFFFU;
tmp = to-3L;
i = from;
if (i<=tmp) for (;; i++) {
crc = X2C_LSH(crc,16,-8)^CRCTAB[(uint32_t)((crc^(uint16_t)(uint8_t)frame[i])&0xFFU)];
if (i==tmp) break;
} /* end for */
return frame[to-1L]==(char)crc && frame[to-2L]==(char)X2C_LSH(crc,
16,-8);
} /* end crcrs() */
static int32_t getint32(const byte frame[], uint32_t frame_len,
uint32_t p)
{
uint32_t n;
uint32_t i;
n = 0UL;
for (i = 3UL;; i--) {
n = n*256UL+(uint32_t)(uint8_t)frame[p+i];
if (i==0UL) break;
} /* end for */
return (int32_t)n;
} /* end getint32() */
static uint32_t getcard16(const byte frame[], uint32_t frame_len,
uint32_t p)
{
return (uint32_t)(uint8_t)frame[p]+256UL*(uint32_t)(uint8_t)
frame[p+1UL];
} /* end getcard16() */
static int32_t getint16(const byte frame[], uint32_t frame_len,
uint32_t p)
{
uint32_t n;
n = (uint32_t)(uint8_t)frame[p]+256UL*(uint32_t)(uint8_t)
frame[p+1UL];
if (n>=32768UL) return (int32_t)(n-65536UL);
return (int32_t)n;
} /* end getint16() */
static void wgs84r(double x, double y, double z,
double * lat, double * long0,
double * heig)
{
double sl;
double ct;
double st;
double t;
double rh;
double xh;
double h;
h = x*x+y*y;
if (h>0.0) {
rh = (double)sqrt((float)h);
xh = x+rh;
*long0 = atang2(xh, y)*2.0;
if (*long0>3.1415926535898) *long0 = *long0-6.2831853071796;
t = (double)atan((float)(X2C_DIVL(z*1.003364089821,
rh)));
st = (double)sin((float)t);
ct = (double)cos((float)t);
*lat = (double)atan((float)
(X2C_DIVL(z+4.2841311513312E+4*st*st*st,
rh-4.269767270718E+4*ct*ct*ct)));
sl = (double)sin((float)*lat);
*heig = X2C_DIVL(rh,(double)cos((float)*lat))-(double)(X2C_DIVR(6.378137E+6f,
sqrt((float)(1.0-6.6943799901413E-3*sl*sl))));
}
else {
*lat = 0.0;
*long0 = 0.0;
*heig = 0.0;
}
/* lat:=atan(z/(rh*(1.0 - E2))); */
/* heig:=sqrt(h + z*z) - EARTHA; */
} /* end wgs84r() */
#endif
static int32_t reedsolomon92(uint8_t *buf, uint32_t buf_len)
{
uint32_t i;
int32_t res;
uint8_t b[256];
uint32_t eraspos[24];
for (i = 0UL; i<=255UL; i++) {
b[i] = 0;
} /* end for */
for (i = 0UL; i<=209UL; i++) {
b[230UL-i] = buf[i+6UL];
} /* end for */
for (i = 0UL; i<=23UL; i++) {
b[254UL-i] = buf[i+216UL];
} /* end for */
res = decodersc((char *)b, eraspos, 0L);
if (res>0L && res<=12L) {
for (i = 0UL; i<=209UL; i++) {
buf[i+6UL] = b[230UL-i];
} /* end for */
for (i = 0UL; i<=23UL; i++) {
buf[i+216UL] = b[254UL-i];
} /* end for */
}
return res;
} /* end reedsolomon92() */
void printRaw(uint8_t *data, int len)
{
char buf[3];
int i;
for(i=0; i<len; i++) {
snprintf(buf, 3, "%02X", data[i]);
Serial.print(buf);
}
Serial.println();
}
void RS92::decodeframe92(uint8_t *data)
{
//uint32_t gpstime;
//uint32_t flen;
//uint32_t j;
int32_t corr;
corr = reedsolomon92(data, 301ul);
//int calok;
//int mesok;
//uint32_t calibok;
lastFrame = (dataptr==data1)?1:2;
Serial.printf("rs corr is %d --- data:%p data1:%p data2:%p lastframe=%d\n", corr, data, data1, data2, lastFrame);
dataptr = (dataptr==data1)?data2:data1;
//print_frame(data, 240);
#if 0
/* from sondemod*/
int p=6;
while(1) {
uint8_t typ = data[p];
if(typ==0xff) break;
++p;
int len = ((uint32_t)data[p])*2 + 2;
Serial.printf("type %c: len=%d\n", typ, len);
//printRaw(data+p, len+2);
if(len>240) {
Serial.print("RS92 frame too long: ");
Serial.println(len);
break;
}
++p;
j=0;
uint16_t crc = 0xFFFF;
while(j<len) {
if(j < len-2) {
for(int ic = 0; ic<=7; ic++) {
if (((0x8000&crc)!=0) != ( ((1<<(7-ic))&data[p])!=0 )) {
crc <<= 1;
crc ^= 0x1021;
} else {
crc <<= 1;
}
}
}
++p;
++j;
if(p>240) {
Serial.println("eof");
return;
}
}
if ( (((uint8_t)(crc&0xff)) != data[p-2]) || (((uint8_t)(crc>>8)) != data[p-1])) {
Serial.printf("************ crc error: expected %04x\n",crc);
continue;
}
switch(typ) {
case 'e':
Serial.println("cal ");
//docalib(sf, 256, objname, 9, &contextr9, &mhz, &frameno);
// ...
break;
case 'i':
if(calok && calibok==0xffffffff) {
//domes(sf, 256, &hp, &hyg, &temp)
mesok = 1;
}
break;
case 'g':
Serial.println("gps ");
if(1||calok) {
//dogps(data+p-len, 256, &contextr9, &contextr9.timems, &gpstime);
}
break;
case 'h':
Serial.println("data "); break;
if(data[p+2]!=3) Serial.println("aux ");
// ..
break;
}
}
#endif
} /* end decodeframe92() */
void RS92::printRaw(uint8_t *data, int len)
{
char buf[3];
int i;
for(i=0; i<len; i++) {
snprintf(buf, 3, "%02X", data[i]);
Serial.print(buf);
}
Serial.println();
}
#if 0
// I guess this is old copy&paste stuff from RS41??
int RS92::bitsToBytes(uint8_t *bits, uint8_t *bytes, int len)
{
int i;
for(i=0; i<len*4; i++) {
bytes[i/8] = (bytes[i/8]<<1) | (bits[i]?1:0);
}
bytes[(i-1)/8] &= 0x0F;
}
static unsigned char lookup[16] = {
0x0, 0x8, 0x4, 0xc, 0x2, 0xa, 0x6, 0xe,
0x1, 0x9, 0x5, 0xd, 0x3, 0xb, 0x7, 0xf, };
static uint8_t reverse(uint8_t n) {
return (lookup[n&0x0f] << 4) | lookup[n>>4];
}
static uint8_t scramble[64] = {150U,131U,62U,81U,177U,73U,8U,152U,50U,5U,89U,
14U,249U,68U,198U,38U,33U,96U,194U,234U,121U,93U,109U,161U,
84U,105U,71U,12U,220U,232U,92U,241U,247U,118U,130U,127U,7U,
153U,162U,44U,147U,124U,48U,99U,245U,16U,46U,97U,208U,188U,
180U,182U,6U,170U,244U,35U,120U,110U,59U,174U,191U,123U,76U,
193U};
#endif
void RS92::stobyte92(uint8_t b)
{
dataptr[rxp] = b;
if(rxp>=5 || b=='*') rxp++; else rxp=0;
if(rxp==6) { // header detected
headerDetected = 1;
}
if(rxp>=240) { // frame complete... (240 byte)
rxp=0;
//printRaw(data, 240);
decodeframe92(dataptr);
haveNewFrame = 1;
}
} /* end stobyte92() */
uint32_t rxdata;
bool rxsearching=true;
// search for
// 101001100110011010011010011001100110100110101010100110101001
// 1010011001100110100110100110 0110.0110 1001.1010 1010.1001 1010.1001 => 0x669AA9A9
void RS92::process8N1data(uint8_t dt)
{
for(int i=0; i<8; i++) {
uint8_t d = (dt&0x80)?1:0;
rxdata = (rxdata<<1) | d;
if((rxbitc&1)==1) { rxbyte = (rxbyte>>1) + (d<<9); } // mancester decoded data
dt <<= 1;
//
if(rxsearching) {
if(rxdata == 0x669AA9A9) {
rxsearching = false;
rxbitc = 0;
rxp = 6;
int rssi=sx1278.getRSSI();
int fei=sx1278.getFEI();
int afc=sx1278.getAFC();
Serial.print("Test: RSSI="); Serial.print(rssi);
Serial.print(" FEI="); Serial.print(fei);
Serial.print(" AFC="); Serial.println(afc);
sonde.si()->rssi = rssi;
sonde.si()->afc = afc;
}
} else {
rxbitc = (rxbitc+1)%20;
if(rxbitc == 0) { // got startbit, 8 data bit, stop bit
//Serial.printf("%03x ",rxbyte);
dataptr[rxp++] = (rxbyte>>1)&0xff;
if(rxp==7 && dataptr[6] != 0x65) {
Serial.printf("wrong start: %02x\n",dataptr[6]);
rxsearching = true;
}
if(rxp>=240) {
rxsearching = true;
decodeframe92(dataptr);
haveNewFrame = 1;
}
}
}
}
}
void process8N1dataOrig(uint8_t data)
{
// data contains 8 bits (after mancester encoding; 4 real bit), big endian
for(int i=0; i<4; i++) {
uint8_t d = (data&0x80)?1:0;
data = data << 2;
rxbyte = (rxbyte>>1) + (d<<8);
int maxk = 0;
int max0 = 0;
for(int k = 0; k< 10; k++) {
int n = asynst[k] - asynst[(k+1)%10];
if(abs(n)>abs(max0)) {
max0 = n;
maxk = k;
}
}
//Serial.printf("<%d,%d,%d>",max0,maxk,rxbitc);
if(rxbitc == maxk) {
if(max0<0) { rxbyte = rxbyte ^ 0xFF; }
/////TODO stobyte92( rxbyte&0xff );
}
//Serial.printf("%d:",asynst[rxbitc]);
if(d) {
asynst[rxbitc] += (32767-asynst[rxbitc])/16;
} else {
asynst[rxbitc] -= (32767+asynst[rxbitc])/16;
}
//Serial.printf("%d ",asynst[rxbitc]);
rxbitc = (rxbitc+1) % 10;
}
}
int RS92::receive() {
unsigned long t0 = millis();
Serial.printf("RS92::receive() start at %ld\n",t0);
while( millis() - t0 < 1000 ) {
uint8_t value = sx1278.readRegister(REG_IRQ_FLAGS2);
if ( bitRead(value, 7) ) {
Serial.println("FIFO full");
}
if ( bitRead(value, 4) ) {
Serial.println("FIFO overflow");
}
if ( bitRead(value, 2) == 1 ) {
Serial.println("FIFO: ready()");
sx1278.clearIRQFlags();
}
if(bitRead(value, 6) == 0) { // while FIFO not empty
byte data = sx1278.readRegister(REG_FIFO);
//Serial.printf("%02x",data);
process8N1data(data);
value = sx1278.readRegister(REG_IRQ_FLAGS2);
} else {
if(headerDetected) {
t0 = millis(); // restart timer... don't time out if header detected...
headerDetected = 0;
}
if(haveNewFrame) {
Serial.printf("RS92::receive(): new frame complete after %ldms\n", millis()-t0);
haveNewFrame = 0;
return RX_OK;
}
delay(2);
}
}
Serial.printf("RS92::receive() timed out\n");
return RX_TIMEOUT; // TODO RX_OK;
}
#define RS92MAXLEN (240)
int RS92::waitRXcomplete() {
// called after complete...
Serial.printf("decoding frame %d\n", lastFrame);
print_frame(lastFrame==1?data1:data2, 240);
SondeInfo *si = sonde.sondeList+rxtask.receiveSonde;
si->lat = gpx.lat;
si->lon = gpx.lon;
si->alt = gpx.alt;
si->vs = gpx.vU;
si->hs = gpx.vH;
si->dir = gpx.vD;
si->validPos = 0x3f;
memcpy(si->id, gpx.id, 9);
memcpy(si->ser, gpx.id, 9);
si->validID = true;
si->frame = gpx.frnr;
si->sats = gpx.k;
si->time = (gpx.gpssec/1000) + 86382 + gpx.week*604800 + 315878400UL;
si->validTime = true;
#if 0
int res=0;
uint32_t t0 = millis();
while( rxtask.receiveResult == 0xFFFF && millis()-t0 < 2000) { delay(20); }
if( rxtask.receiveResult<0 || rxtask.receiveResult==RX_TIMEOUT) {
res = RX_TIMEOUT;
} else if ( rxtask.receiveResult==0) {
res = RX_OK;
} else {
res = RX_ERROR;
}
rxtask.receiveResult = 0xFFFF;
Serial.printf("RS92::waitRXcomplete returning %d (%s)\n", res, RXstr[res]);
return res;
#endif
return 0;
}
#if 0
int oldwaitRXcomplete() {
Serial.println("RS92: receive frame...\n");
sx1278receiveData = true;
delay(6000); // done in other task....
//sx1278receiveData = false;
#if 0
//sx1278.setPayloadLength(518-8); // Expect 320-8 bytes or 518-8 bytes (8 byte header)
//sx1278.setPayloadLength(0); // infinite for now...
////// test code for continuous reception
// sx1278.receive(); /// active FSK RX mode -- already done above...
uint8_t value = sx1278.readRegister(REG_IRQ_FLAGS2);
unsigned long previous = millis();
byte ready=0;
uint32_t wait = 8000;
// while not yet done or FIFO not yet empty
// bit 6: FIFO Empty
// bit 2 payload ready
int by=0;
while( (!ready || bitRead(value,6)==0) && (millis() - previous < wait) )
{
if( bitRead(value, 7) ) { Serial.println("FIFO full"); }
if( bitRead(value, 4) ) { Serial.println("FIFO overflow"); }
if( bitRead(value,2)==1 ) ready=1;
if( bitRead(value, 6) == 0 ) { // FIFO not empty
byte data = sx1278.readRegister(REG_FIFO);
process8N1data(data);
by++;
#if 0
if(di==1) {
int rssi=getRSSI();
int fei=getFEI();
int afc=getAFC();
Serial.print("Test: RSSI="); Serial.println(rssi);
Serial.print("Test: FEI="); Serial.println(fei);
Serial.print("Test: AFC="); Serial.println(afc);
sonde.si()->rssi = rssi;
sonde.si()->afc = afc;
}
if(di>520) {
// TODO
Serial.println("TOO MUCH DATA");
break;
}
previous = millis(); // reset timeout after receiving data
#endif
}
value = sx1278.readRegister(REG_IRQ_FLAGS2);
}
Serial.printf("processed %d bytes before end/timeout\n", by);
#endif
/////
#if 0
int e = sx1278.receivePacketTimeout(1000, data+8);
if(e) { Serial.println("TIMEOUT"); return RX_TIMEOUT; } //if timeout... return 1
printRaw(data, RS92MAXLEN);
//for(int i=0; i<RS92MAXLEN; i++) { data[i] = reverse(data[i]); }
//printRaw(data, MAXLEN);
//for(int i=0; i<RS92MAXLEN; i++) { data[i] = data[i] ^ scramble[i&0x3F]; }
//printRaw(data, MAXLEN);
//int res = decode41(data, RS92MAXLEN);
#endif
int res=0;
return res==0 ? RX_OK : RX_ERROR;
}
#endif
RS92 rs92 = RS92();

96
libraries/SondeLib/RS92.h Executable file
View File

@ -0,0 +1,96 @@
/*
* RS92.h
* Functions for decoding RS92 sondes with SX127x chips
* Copyright (C) 2019 Hansi Reiser, dl9rdz
*
* SPDX-License-Identifier: GPL-2.0+
*/
#ifndef RS92_h
#define RS92_h
#include <stdlib.h>
#include <stdint.h>
#include <Arduino.h>
#ifndef inttypes_h
#include <inttypes.h>
#endif
struct CONTEXTR9 {
char calibdata[512];
uint32_t calibok;
char mesok;
char posok;
char framesent;
double lat;
double lon;
double heig;
double speed;
double dir;
double climb;
double lastlat;
double laslong;
double lastalt;
double lastspeed;
double lastdir;
double lastclb;
float hrmsc;
float vrmsc;
double hp;
double hyg;
double temp;
double ozontemp;
double ozon;
uint32_t goodsats;
uint32_t timems;
uint32_t framenum;
};
/* Main class */
class RS92
{
private:
void process8N1data(uint8_t data);
void stobyte92(uint8_t byte);
void decodeframe92(uint8_t *data);
#if 0
void dogps(const uint8_t *sf, int sf_len,
struct CONTEXTR9 * cont, uint32_t * timems,
uint32_t * gpstime);
#endif
uint32_t bits2val(const uint8_t *bits, int len);
void printRaw(uint8_t *data, int len);
int bitsToBytes(uint8_t *bits, uint8_t *bytes, int len);
int decode92(byte *data, int maxlen);
uint8_t hamming_conf[ 7*8]; // 7*8=56
uint8_t hamming_dat1[13*8]; // 13*8=104
uint8_t hamming_dat2[13*8];
uint8_t block_conf[ 7*4]; // 7*4=28
uint8_t block_dat1[13*4]; // 13*4=52
uint8_t block_dat2[13*4];
uint8_t H[4][8] = // extended Hamming(8,4) particy check matrix
{{ 0, 1, 1, 1, 1, 0, 0, 0},
{ 1, 0, 1, 1, 0, 1, 0, 0},
{ 1, 1, 0, 1, 0, 0, 1, 0},
{ 1, 1, 1, 0, 0, 0, 0, 1}};
uint8_t He[8] = { 0x7, 0xB, 0xD, 0xE, 0x8, 0x4, 0x2, 0x1}; // Spalten von H:
// 1-bit-error-Syndrome
boolean initialized = false;
public:
RS92();
int setup(float frequency);
int receive();
int waitRXcomplete();
int use_ecc = 1;
};
extern RS92 rs92;
#endif

113
libraries/SondeLib/Scanner.cpp Executable file
View File

@ -0,0 +1,113 @@
#include "Scanner.h"
#include <SX1278FSK.h>
#include <U8x8lib.h>
#include "Sonde.h"
#include "Display.h"
#define CHANBW 10
#define PIXSAMPL (50/CHANBW)
#define SMOOTH 3
//#define STARTF 401000000
#define NCHAN ((int)(6000/CHANBW))
double STARTF = (sonde.config.startfreq * 1000000);
//int CHANBW = (sonde.config.channelbw);
//int NCHAN = ((int)(6000/CHANBW));
//int PIXSAMPL = (50/CHANBW);
int scanresult[NCHAN];
int scandisp[NCHAN/PIXSAMPL];
#define PLOT_N 128
#define TICK1 (128/6)
#define TICK2 (TICK1/4)
//#define PLOT_MIN -250
#define PLOT_MIN (sonde.config.noisefloor*2)
#define PLOT_SCALE(x) (x<PLOT_MIN?0:(x-PLOT_MIN)/2)
const byte tilepatterns[9]={0,0x80,0xC0,0xE0,0xF0,0xF8,0xFC,0xFE,0xFF};
void Scanner::fillTiles(uint8_t *row, int value) {
for(int y=0; y<8; y++) {
int nbits = value - 8*(7-y);
if(nbits<0) { row[8*y]=0; continue; }
if(nbits>=8) { row[8*y]=255; continue; }
row[8*y] = tilepatterns[nbits];
}
}
/*
* There are 16*8 columns to plot, NPLOT must be lower than that
* currently, we use 128 * 50kHz channels
* There are 8*8 values to plot; MIN is bottom end,
*/
uint8_t tiles[16] = { 0x0f,0x0f,0x0f,0x0f,0xf0,0xf0,0xf0,0xf0, 1, 3, 7, 15, 31, 63, 127, 255};
void Scanner::plotResult()
{
uint8_t row[8*8];
for(int i=0; i<PLOT_N; i+=8) {
for(int j=0; j<8; j++) {
fillTiles(row+j, PLOT_SCALE(scandisp[i+j]));
if( ((i+j)%TICK1)==0) { row[j] |= 0x07; }
if( ((i+j)%TICK2)==0) { row[j] |= 0x01; }
}
for(int y=0; y<8; y++) {
if(sonde.config.marker && y==1) {
// don't overwrite MHz marker text
if(i<3*8 || (i>=7*8&&i<10*8) || i>=13*8) continue;
}
disp.rdis->drawTile(i/8, y, 1, row+8*y);
}
}
}
void Scanner::scan()
{
// Configure
sx1278.writeRegister(REG_PLL_HOP, 0x80); // FastHopOn
sx1278.setRxBandwidth(CHANBW*1000);
sx1278.writeRegister(REG_RSSI_CONFIG, SMOOTH&0x07);
sx1278.setFrequency(STARTF);
sx1278.writeRegister(REG_OP_MODE, FSK_RX_MODE);
delay(20);
unsigned long start = millis();
uint32_t lastfrf=-1;
for(int iter=0; iter<2; iter++) { // two interations, to catch all RS41 transmissions
for(int i=0; i<NCHAN; i++) {
float freq = STARTF + 1000.0*i*CHANBW;
uint32_t frf = freq * 1.0 * (1<<19) / SX127X_CRYSTAL_FREQ;
if( (lastfrf>>16)!=(frf>>16) ) {
sx1278.writeRegister(REG_FRF_MSB, (frf&0xff0000)>>16);
}
if( ((lastfrf&0x00ff00)>>8) != ((frf&0x00ff00)>>8) ) {
sx1278.writeRegister(REG_FRF_MID, (frf&0x00ff00)>>8);
}
sx1278.writeRegister(REG_FRF_LSB, (frf&0x0000ff));
lastfrf = frf;
// Wait TS_HOP (20us) + TS_RSSI ( 2^(SMOOTH+1) / 4 / CHANBW us)
int wait = 20 + 1000*(1<<(SMOOTH+1))/4/CHANBW;
delayMicroseconds(wait+5);
int rssi = -(int)sx1278.readRegister(REG_RSSI_VALUE_FSK);
if(iter==0) { scanresult[i] = rssi; } else {
if(rssi>scanresult[i]) scanresult[i]=rssi;
}
}
}
unsigned long duration = millis()-start;
Serial.print("Scan time: ");
Serial.println(duration);
for(int i=0; i<NCHAN; i+=PIXSAMPL) {
scandisp[i/PIXSAMPL]=scanresult[i];
for(int j=1; j<PIXSAMPL; j++) { scandisp[i/PIXSAMPL]+=scanresult[i+j]; }
//for(int j=1; j<PIXSAMPL; j++) { if(scanresult[i+j]>scandisp[i/PIXSAMPL]) scandisp[i/PIXSAMPL] = scanresult[i+j]; }
Serial.print(scanresult[i]); Serial.print(", ");
}
Serial.println("\n");
for(int i=0; i<NCHAN/PIXSAMPL; i++) {
scandisp[i]/=PIXSAMPL;
Serial.print(scandisp[i]); Serial.print(", ");
}
Serial.println("\n");
}
Scanner scanner = Scanner();

24
libraries/SondeLib/Scanner.h Executable file
View File

@ -0,0 +1,24 @@
#ifndef _SCANNER_H
#define _SCANNER_H
#include <stdlib.h>
#include <stdint.h>
#include <Arduino.h>
#ifndef inttypes_h
#include <inttypes.h>
#endif
class Scanner
{
private:
void fillTiles(uint8_t *row, int value);
public:
void plotResult();
void scan(void);
};
extern Scanner scanner;
#endif

689
libraries/SondeLib/Sonde.cpp Executable file
View File

@ -0,0 +1,689 @@
#include <U8x8lib.h>
#include <U8g2lib.h>
#include "Sonde.h"
#include "RS41.h"
#include "RS92.h"
#include "DFM.h"
#include "M10.h"
#include "SX1278FSK.h"
#include "Display.h"
#include <Wire.h>
#define SDCARD
#ifdef SDCARD
#include "sd.h"
#endif
int snOn=0;
extern SX1278FSK sx1278;
RXTask rxtask = { -1, -1, -1, 0xFFFF, 0 };
const char *evstring[]={"NONE", "KEY1S", "KEY1D", "KEY1M", "KEY1L", "KEY2S", "KEY2D", "KEY2M", "KEY2L",
"VIEWTO", "RXTO", "NORXTO", "(max)"};
const char *RXstr[]={"RX_OK", "RX_TIMEOUT", "RX_ERROR", "RX_UNKNOWN"};
int fingerprintValue[]={ 17, 31, 64, 4, 55, 48, 23, 128+23, -1 };
const char *fingerprintText[]={
"TTGO T-Beam (new version 1.0), I2C not working after powerup, assuming 0.9\" OLED@21,22",
"TTGO LORA32 v2.1_1.6 (0.9\" OLED@21,22)",
"TTGO LORA v1.0 (0.9\" OLED@4,15)",
"Heltec v1/v2 (0.9\"OLED@4,15)",
"TTGO T-Beam (old version), 0.9\" OLED@21,22",
"TTGO T-Beam (old version), SPI TFT@4,21,22",
"TTGO T-Beam (new version 1.0), 0.9\" OLED@21,22",
"TTGO T-Beam (new version 1.0), SPI TFT@4,13,14",
};
int getKeyPressEvent(); /* in RX_FSK.ino */
/* Task model:
* There is a background task for all SX1278 interaction.
* - On startup and on each mode/frequency change (requested by setting requestNextSonde
* to an sonde index >=0) it calls Sonde::setup(), which will call the new decoder's
* setup function. Setup will update the value currentSonde.
* - Periodically it calls Sonde::receive(), which calls the current decoder's receive()
* function. It should return control to the SX1278 main loop at least once per second.
* It will also set the internal variable receiveResult. The decoder's receive function
* must make sure that there are no FIFI overflows in the SX1278.
* - the Arduino main loop will call the waitRXcomplete function, which should return as
* soon as there is some new data to display, or no later than after 1s, returning the
* value of receiveResult (or timeout, if receiveResult was not set within 1s). It
* should also return immediately if there is some keyboard input.
*/
int initlevels[40];
Sonde::Sonde() {
for (int i = 0; i < 39; i++) {
initlevels[i] = gpio_get_level((gpio_num_t)i);
}
}
void Sonde::defaultConfig() {
fingerprint = initlevels[4];
fingerprint = (fingerprint<<1) | initlevels[12];
fingerprint = (fingerprint<<1) | initlevels[16];
fingerprint = (fingerprint<<1) | initlevels[17];
fingerprint = (fingerprint<<1) | initlevels[21];
fingerprint = (fingerprint<<1) | initlevels[22];
fingerprint = (fingerprint<<1) | initlevels[23];
Serial.printf("Board fingerprint is %d\n", fingerprint);
sondeList = (SondeInfo *)malloc((MAXSONDE+1)*sizeof(SondeInfo));
memset(sondeList, 0, (MAXSONDE+1)*sizeof(SondeInfo));
config.touch_thresh = 70;
config.led_pout = -1;
config.power_pout = -1;
config.spectrum=10;
// Try autodetecting board type
// Seems like on startup, GPIO4 is 1 on v1 boards, 0 on v2.1 boards?
config.gpsOn=0;
config.gps_rxd = -1;
config.gps_txd = -1;
strcpy(config.gps_lat,"43.591");
strcpy(config.gps_lon,"7.100");
config.gps_alt=123;
config.oled_rst = 16;
config.disptype = 0;
config.oled_orient = 1;
config.button2_axp = 0;
config.norx_timeout = 20;
if(initlevels[16]==0) {
config.oled_sda = 4;
config.oled_scl = 15;
config.button_pin = 0;
config.button2_pin = T4 + 128; // T4 == GPIO13
config.power_pout = 21; // for Heltec v2
config.led_pout = 2;
Serial.println("Autoconfig: looks like TTGO v1 / Heltec v1/V2 board");
} else {
config.oled_sda = 21;
config.oled_scl = 22;
if(initlevels[17]==0) { // T-Beam
if(initlevels[12]==0) { // T-Beam v1.0
Serial.println("Autoconfig: looks like T-Beam 1.0 board");
config.button_pin = 38;
config.button2_pin = 15 + 128; //T4 + 128; // T4 = GPIO13
// Maybe in future use as default only PWR as button2?
//config.button2_pin = 255;
config.button2_axp = 1;
config.gps_rxd = 34;
// Check for I2C-Display@21,22
#define SSD1306_ADDRESS 0x3c
Wire.begin(21, 22);
Wire.beginTransmission(SSD1306_ADDRESS);
byte err = Wire.endTransmission();
delay(100); // otherwise its too fast?!
Wire.beginTransmission(SSD1306_ADDRESS);
err = Wire.endTransmission();
if(err!=0 && fingerprint!=17) { // hmm. 17 after powerup with oled commected and no i2c answer!?!?
fingerprint |= 128;
Serial.println("no I2C display found, assuming large TFT display\n");
// CS=0, RST=14, RS=2, SDA=4, CLK=13
Serial.println("... with large TFT display\n");
config.disptype = 1;
config.oled_sda = 4;
config.oled_scl = 13;
config.oled_rst = 14;
config.spectrum = -1; // no spectrum for now on large display
} else {
// OLED display, pins 21,22 ok...
config.disptype = 0;
Serial.println("... with small OLED display\n");
}
} else {
Serial.println("Autoconfig: looks like T-Beam v0.7 board");
config.button_pin = 39;
config.button2_pin = T4 + 128; // T4 == GPIO13
config.gps_rxd = 12;
// Check if we possibly have a large display
if(initlevels[21]==0) {
Serial.println("Autoconfig: looks like T-Beam v0.7 board with large TFT display");
config.disptype = 1;
config.oled_sda = 4;
config.oled_scl = 21;
config.oled_rst = 22;
config.spectrum = -1; // no spectrum for now on large display
}
}
} else {
config.button_pin = 2 + 128; // GPIO2 / T2
config.button2_pin = 14 + 128; // GPIO14 / T6
config.led_pout = 25;
}
}
//
config.noisefloor = -125;
strcpy(config.call,"NOCALL");
strcpy(config.passcode, "---");
strcpy(config.mdnsname, "radiosonde");
strcpy(config.vbatmax,"1.84");
strcpy(config.vbatmin,"1.64");
config.sdOn=0;
config.buzzerOn=0;
config.buzzerFreq=700;
config.buzzerPort=12;
config.dbsmetre=0;
config.maxsonde=15;
config.debug=0;
config.wifi=1;
config.wifiap=1;
config.display[0]=0;
config.display[1]=1;
config.display[2]=-1;
config.startfreq=400;
config.channelbw=10;
config.marker=0;
config.showafc=0;
config.freqofs=0;
config.rs41.agcbw=12500;
config.rs41.rxbw=6300;
config.rs92.rxbw=12500;
config.rs92.alt2d=480;
config.dfm.agcbw=20800;
config.dfm.rxbw=10400;
config.udpfeed.active = 1;
config.udpfeed.type = 0;
strcpy(config.udpfeed.host, "fra1od.fr.to");
strcpy(config.udpfeed.symbol, "/O");
config.udpfeed.port = 14580;
config.udpfeed.highrate = 1;
config.udpfeed.idformat = ID_DFMGRAW;
config.tcpfeed.active = 0;
config.tcpfeed.type = 1;
strcpy(config.tcpfeed.host, "radiosondy.info");
strcpy(config.tcpfeed.symbol, "/O");
config.tcpfeed.port = 14580;
config.tcpfeed.highrate = 10;
config.tcpfeed.idformat = ID_DFMDXL;
config.kisstnc.active = 0;
}
void Sonde::setConfig(const char *cfg) {
while(*cfg==' '||*cfg=='\t') cfg++;
if(*cfg=='#') return;
char *s = strchr(cfg,'=');
if(!s) return;
char *val = s+1;
*s=0; s--;
while(s>cfg && (*s==' '||*s=='\t')) { *s=0; s--; }
Serial.printf("configuration option '%s'=%s \n", cfg, val);
if(strcmp(cfg,"noisefloor")==0) {
config.noisefloor = atoi(val);
if(config.noisefloor==0) config.noisefloor=-130;
} else if(strcmp(cfg,"call")==0) {
strncpy(config.call, val, 9);
config.call[9]=0;
} else if(strcmp(cfg,"passcode")==0) {
strncpy(config.passcode, val, 9);
} else if(strcmp(cfg,"button_pin")==0) {
config.button_pin = atoi(val);
} else if(strcmp(cfg,"button2_pin")==0) {
config.button2_pin = atoi(val);
} else if(strcmp(cfg,"button2_axp")==0) {
config.button2_axp = atoi(val);
} else if(strcmp(cfg,"touch_thresh")==0) {
config.touch_thresh = atoi(val);
} else if(strcmp(cfg,"led_pout")==0) {
config.led_pout = atoi(val);
} else if(strcmp(cfg,"power_pout")==0) {
config.power_pout = atoi(val);
} else if(strcmp(cfg,"disptype")==0) {
config.disptype = atoi(val);
} else if(strcmp(cfg,"oled_sda")==0) {
config.oled_sda = atoi(val);
} else if(strcmp(cfg,"oled_scl")==0) {
config.oled_scl = atoi(val);
} else if(strcmp(cfg,"oled_rst")==0) {
config.oled_rst = atoi(val);
} else if(strcmp(cfg,"oled_orient")==0) {
config.oled_orient = atoi(val);
} else if(strcmp(cfg,"gpsOn")==0) {
config.gpsOn = atoi(val);
} else if(strcmp(cfg,"gps_rxd")==0) {
config.gps_rxd = atoi(val);
} else if(strcmp(cfg,"gps_txd")==0) {
config.gps_txd = atoi(val);
} else if(strcmp(cfg,"gps_lat")==0) {
strncpy(config.gps_lat, val, 8);
} else if(strcmp(cfg,"gps_lon")==0) {
strncpy(config.gps_lon, val, 8);
} else if(strcmp(cfg,"gps_alt")==0) {
config.gps_alt = atoi(val);
} else if(strcmp(cfg,"maxsonde")==0) {
config.maxsonde = atoi(val);
if(config.maxsonde>MAXSONDE) config.maxsonde=MAXSONDE;
} else if(strcmp(cfg,"debug")==0) {
config.debug = atoi(val);
} else if(strcmp(cfg,"wifi")==0) {
config.wifi = atoi(val);
} else if(strcmp(cfg,"wifiap")==0) {
config.wifiap = atoi(val);
} else if(strcmp(cfg,"mdnsname")==0) {
strncpy(config.mdnsname, val, 14);
} else if(strcmp(cfg,"vbatmax")==0) {
strncpy(config.vbatmax,val,5);
} else if(strcmp(cfg,"vbatmin")==0) {
strncpy(config.vbatmin,val,5);
} else if(strcmp(cfg,"sdOn")==0) {
config.sdOn=atoi(val);
} else if(strcmp(cfg,"buzzerOn")==0) {
config.buzzerOn=atoi(val);
} else if(strcmp(cfg,"buzzerPort")==0) {
config.buzzerPort=atoi(val);
} else if(strcmp(cfg,"buzzerFreq")==0) {
config.buzzerFreq=atoi(val);
} else if(strcmp(cfg,"dbsmetre")==0) {
config.dbsmetre=atoi(val);
} else if(strcmp(cfg,"display")==0) {
int i = 0;
char *ptr;
while(val) {
ptr = strchr(val,',');
if(ptr) *ptr = 0;
config.display[i++] = atoi(val);
val = ptr?ptr+1:NULL;
Serial.printf("appending value %d next is %s\n", config.display[i-1], val?val:"");
}
config.display[i] = -1;
} else if (strcmp(cfg, "norx_timeout")==0) {
config.norx_timeout = atoi(val);
} else if(strcmp(cfg,"startfreq")==0) {
config.startfreq = atoi(val);
} else if(strcmp(cfg,"channelbw")==0) {
config.channelbw = atoi(val);
} else if(strcmp(cfg,"spectrum")==0) {
config.spectrum = atoi(val);
} else if(strcmp(cfg,"marker")==0) {
config.marker = atoi(val);
} else if(strcmp(cfg,"showafc")==0) {
config.showafc = atoi(val);
} else if(strcmp(cfg,"freqofs")==0) {
config.freqofs = atoi(val);
} else if(strcmp(cfg,"rs41.agcbw")==0) {
config.rs41.agcbw = atoi(val);
} else if(strcmp(cfg,"rs41.rxbw")==0) {
config.rs41.rxbw = atoi(val);
} else if(strcmp(cfg,"dfm.agcbw")==0) {
config.dfm.agcbw = atoi(val);
} else if(strcmp(cfg,"dfm.rxbw")==0) {
config.dfm.rxbw = atoi(val);
} else if(strcmp(cfg,"rs92.alt2d")==0) {
config.rs92.alt2d= atoi(val);
} else if(strcmp(cfg,"kisstnc.active")==0) {
config.kisstnc.active = atoi(val);
} else if(strcmp(cfg,"kisstnc.idformat")==0) {
config.kisstnc.idformat = atoi(val);
} else if(strcmp(cfg,"rs92.rxbw")==0) {
config.rs92.rxbw = atoi(val);
} else if(strcmp(cfg,"axudp.active")==0) {
config.udpfeed.active = atoi(val)>0;
} else if(strcmp(cfg,"axudp.host")==0) {
strncpy(config.udpfeed.host, val, 63);
} else if(strcmp(cfg,"axudp.port")==0) {
config.udpfeed.port = atoi(val);
} else if(strcmp(cfg,"axudp.symbol")==0) {
strncpy(config.udpfeed.symbol, val, 3);
} else if(strcmp(cfg,"axudp.highrate")==0) {
config.udpfeed.highrate = atoi(val);
} else if(strcmp(cfg,"axudp.idformat")==0) {
config.udpfeed.idformat = atoi(val);
} else if(strcmp(cfg,"tcp.active")==0) {
config.tcpfeed.active = atoi(val)>0;
} else if(strcmp(cfg,"tcp.host")==0) {
strncpy(config.tcpfeed.host, val, 63);
} else if(strcmp(cfg,"tcp.port")==0) {
config.tcpfeed.port = atoi(val);
} else if(strcmp(cfg,"tcp.symbol")==0) {
strncpy(config.tcpfeed.symbol, val, 3);
} else if(strcmp(cfg,"tcp.highrate")==0) {
config.tcpfeed.highrate = atoi(val);
} else if(strcmp(cfg,"tcp.idformat")==0) {
config.tcpfeed.idformat = atoi(val);
} else {
Serial.printf("Invalid config option '%s'=%s \n", cfg, val);
}
}
void Sonde::setIP(String ip, bool AP) {
ipaddr = ip;
isAP = AP;
}
void Sonde::clearSonde() {
nSonde = 0;
}
void Sonde::addSonde(float frequency, SondeType type, int active, char *launchsite) {
if(nSonde>=config.maxsonde) {
Serial.println("Cannot add another sonde, MAXSONDE reached");
return;
}
Serial.printf("Adding %f - %d - %d - %s\n", frequency, type, active, launchsite);
sondeList[nSonde].type = type;
sondeList[nSonde].freq = frequency;
sondeList[nSonde].active = active;
strncpy(sondeList[nSonde].launchsite, launchsite, 17);
memcpy(sondeList[nSonde].rxStat, "\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3", 18); // unknown/undefined
nSonde++;
}
// called by updateState (only)
void Sonde::nextConfig() {
currentSonde++;
if(currentSonde>=nSonde) {
currentSonde=0;
}
// Skip non-active entries (but don't loop forever if there are no active ones)
for(int i=0; i<config.maxsonde; i++) {
if(!sondeList[currentSonde].active) {
currentSonde++;
if(currentSonde>=nSonde) currentSonde=0;
}
}
}
void Sonde::nextRxSonde() {
rxtask.currentSonde++;
if(rxtask.currentSonde>=nSonde) {
rxtask.currentSonde=0;
}
for(int i=0; i<config.maxsonde; i++) {
if(!sondeList[rxtask.currentSonde].active) {
rxtask.currentSonde++;
if(rxtask.currentSonde>=nSonde) rxtask.currentSonde=0;
}
}
Serial.printf("nextRxSonde: %d\n", rxtask.currentSonde);
}
void Sonde::nextRxFreq(int addkhz) {
// last entry is for the variable frequency
rxtask.currentSonde = nSonde - 1;
sondeList[rxtask.currentSonde].active = 1;
sondeList[rxtask.currentSonde].freq += addkhz*0.001;
if(sondeList[rxtask.currentSonde].freq>406)
sondeList[rxtask.currentSonde].freq = 400;
Serial.printf("nextRxFreq: %d\n", rxtask.currentSonde);
}
SondeInfo *Sonde::si() {
return &sondeList[currentSonde];
}
void Sonde::setup() {
if(rxtask.currentSonde<0 || rxtask.currentSonde>=config.maxsonde) {
Serial.print("Invalid rxtask.currentSonde: ");
Serial.println(rxtask.currentSonde);
rxtask.currentSonde = 0;
}
// update receiver config
Serial.print("\nSonde::setup() on sonde index ");
Serial.println(rxtask.currentSonde);
switch(sondeList[rxtask.currentSonde].type) {
case STYPE_RS41:
rs41.setup(sondeList[rxtask.currentSonde].freq * 1000000);
break;
case STYPE_DFM06:
case STYPE_DFM09:
dfm.setup( sondeList[rxtask.currentSonde].freq * 1000000, sondeList[rxtask.currentSonde].type==STYPE_DFM06?0:1 );
break;
case STYPE_RS92:
rs92.setup( sondeList[rxtask.currentSonde].freq * 1000000);
break;
case STYPE_M10:
m10.setup( sondeList[rxtask.currentSonde].freq * 1000000);
break;
}
// debug
float afcbw = sx1278.getAFCBandwidth();
float rxbw = sx1278.getRxBandwidth();
Serial.printf("AFC BW: %f RX BW: %f\n", afcbw, rxbw);
}
extern void flashLed(int ms);
extern void buzzerLed(int temps);
void Sonde::receive() {
uint16_t res = 0;
SondeInfo *si = &sondeList[rxtask.currentSonde];
switch(si->type) {
case STYPE_RS41:
res = rs41.receive();
break;
case STYPE_RS92:
res = rs92.receive();
break;
case STYPE_M10:
res = m10.receive();
break;
case STYPE_DFM06:
case STYPE_DFM09:
res = dfm.receive();
break;
}
// state information for RX_TIMER / NORX_TIMER events
if(res==0) { // RX OK
flashLed(700);
if(sonde.config.buzzerOn==1) buzzerLed(500);
if(si->lastState != 1) {
si->rxStart = millis();
si->lastState = 1;
}
} else { // RX not ok
if(res==RX_ERROR) {
flashLed(100);
}
Serial.printf("RX result %d, laststate was %d\n", res, si->lastState);
if(si->lastState != 0) {
si->norxStart = millis();
si->lastState = 0;
}
}
// Serial.printf("debug: res was %d, now lastState is %d\n", res, si->lastState);
// we should handle timer events here, because after returning from receive,
// we'll directly enter setup
rxtask.receiveSonde = rxtask.currentSonde; // pass info about decoded sonde to main loop
int event = getKeyPressEvent();
if (!event) event = timeoutEvent(si);
int action = (event==EVT_NONE) ? ACT_NONE : disp.layout->actions[event];
Serial.printf("event %x: action is %x\n", event, action);
// If action is to move to a different sonde index, we do update things here, set activate
// to force the sx1278 task to call sonde.setup(), and pass information about sonde to
// main loop (display update...)
if(action == ACT_NEXTSONDE || action==ACT_PREVSONDE || (action>64&&action<128) ) {
// handled here...
if(action==ACT_NEXTSONDE||action==ACT_PREVSONDE)
nextRxSonde();
else
nextRxFreq( action-64 );
action = ACT_SONDE(rxtask.currentSonde);
if(rxtask.activate==-1) {
// race condition here. maybe better use mutex. TODO
rxtask.activate = action;
}
}
res = (action<<8) | (res&0xff);
Serial.printf("receive Result is %04x\n", res);
// let waitRXcomplete resume...
rxtask.receiveResult = res;
#ifdef SDCARD
if (sonde.config.sdOn==1){
sdwrite();
}
#endif
}
// return (action<<8) | (rxresult)
uint16_t Sonde::waitRXcomplete() {
uint16_t res=0;
uint32_t t0 = millis();
rxloop:
while( rxtask.receiveResult==0xFFFF && millis()-t0 < 3000) { delay(50); }
if( rxtask.receiveResult == RX_UPDATERSSI ) {
rxtask.receiveResult = 0xFFFF;
Serial.print("RSSI update: ");
disp.updateDisplayRSSI();
goto rxloop;
}
if( rxtask.receiveResult==0xFFFF) {
Serial.println("TIMEOUT in waitRXcomplete. Should never happen!\n");
res = RX_TIMEOUT;
} else {
res = rxtask.receiveResult;
}
rxtask.receiveResult = 0xFFFF;
/// TODO: THis has caused an exception when swithcing back to spectrumm...
Serial.printf("waitRXcomplete returning %04x (%s)\n", res, (res&0xff)<4?RXstr[res&0xff]:"");
// currently used only by RS92
switch(sondeList[rxtask.receiveSonde].type) {
case STYPE_RS41:
rs41.waitRXcomplete();
break;
case STYPE_RS92:
rs92.waitRXcomplete();
break;
case STYPE_M10:
m10.waitRXcomplete();
break;
case STYPE_DFM06:
case STYPE_DFM09:
dfm.waitRXcomplete();
break;
}
memmove(sonde.si()->rxStat+1, sonde.si()->rxStat, 17);
sonde.si()->rxStat[0] = res;
return res;
}
uint8_t Sonde::timeoutEvent(SondeInfo *si) {
uint32_t now = millis();
#if 1
Serial.printf("Timeout check: %d - %d vs %d; %d - %d vs %d; %d - %d vs %d\n",
now, si->viewStart, disp.layout->timeouts[0],
now, si->rxStart, disp.layout->timeouts[1],
now, si->norxStart, disp.layout->timeouts[2]);
#endif
Serial.printf("lastState is %d\n", si->lastState);
if(disp.layout->timeouts[0]>=0 && now - si->viewStart >= disp.layout->timeouts[0]) {
Serial.println("View timer expired");
return EVT_VIEWTO;
}
if(si->lastState==1 && disp.layout->timeouts[1]>=0 && now - si->rxStart >= disp.layout->timeouts[1]) {
Serial.println("RX timer expired");
return EVT_RXTO;
}
if(si->lastState==0 && disp.layout->timeouts[2]>=0 && now - si->norxStart >= disp.layout->timeouts[2]) {
Serial.println("NORX timer expired");
return EVT_NORXTO;
}
return 0;
}
uint8_t Sonde::updateState(uint8_t event) {
Serial.printf("Sonde::updateState for event %d\n", event);
// No change
if(event==ACT_NONE) return 0xFF;
// In all cases (new display mode, new sonde) we reset the mode change timers
sonde.sondeList[sonde.currentSonde].viewStart = millis();
sonde.sondeList[sonde.currentSonde].lastState = -1;
// Moving to a different display mode
if (event==ACT_DISPLAY_SPECTRUM || event==ACT_DISPLAY_WIFI) {
// main loop will call setMode() and disable sx1278 background task
return event;
}
int n = event;
if(event==ACT_DISPLAY_DEFAULT) {
n = config.display[1];
} else if(event==ACT_DISPLAY_SCANNER) {
n= config.display[0];
} else if(event==ACT_DISPLAY_NEXT) {
int i;
for(i=0; config.display[i]!=-1; i++) {
if(config.display[i] == disp.layoutIdx) break;
}
if(config.display[i]==-1 || config.display[i+1]==-1) {
//unknown index, or end of list => loop to start
n = config.display[1];
} else {
n = config.display[i+1];
}
}
if(n>=0 && n<ACT_MAXDISPLAY) {
if(n>=disp.nLayouts) {
Serial.println("WARNNG: next layout out of range");
n = config.display[1];
}
Serial.printf("Setting display mode %d\n", n);
disp.setLayout(n);
sonde.clearDisplay();
return 0xFF;
}
// Moving to a different value for currentSonde
// TODO: THis should be done in sx1278 task, not in main loop!!!!!
if(event==ACT_NEXTSONDE) {
sonde.nextConfig();
Serial.printf("advancing to next sonde %d\n", sonde.currentSonde);
return event;
}
if (event==ACT_PREVSONDE) {
// TODO
Serial.printf("previous not supported, advancing to next sonde\n");
sonde.nextConfig();
return ACT_NEXTSONDE;
}
if(event&0x80) {
sonde.currentSonde = (event&0x7F);
return ACT_NEXTSONDE;
}
return 0xFF;
}
void Sonde::updateDisplayPos() {
disp.updateDisplayPos();
}
void Sonde::updateDisplayPos2() {
disp.updateDisplayPos2();
}
void Sonde::updateDisplayID() {
disp.updateDisplayID();
}
void Sonde::updateDisplayRSSI() {
disp.updateDisplayRSSI();
}
void Sonde::updateStat() {
disp.updateStat();
}
void Sonde::updateDisplayRXConfig() {
disp.updateDisplayRXConfig();
}
void Sonde::updateDisplayIP() {
disp.updateDisplayIP();
}
void Sonde::updateDisplay()
{
int t = millis();
disp.updateDisplay();
Serial.printf("updateDisplay took %d ms\n", (int)(millis()-t));
}
void Sonde::clearDisplay() {
disp.rdis->clear();
}
Sonde sonde = Sonde();

265
libraries/SondeLib/Sonde.h Executable file
View File

@ -0,0 +1,265 @@
#ifndef Sonde_h
#define Sonde_h
// RX_TIMEOUT: no header detected
// RX_ERROR: header detected, but data not decoded (crc error, etc.)
// RX_OK: header and data ok
enum RxResult { RX_OK, RX_TIMEOUT, RX_ERROR, RX_UNKNOWN, RX_NOPOS };
#define RX_UPDATERSSI 0xFFFE
// Events that change what is displayed (mode, sondenr)
// Keys:
// 1 Button (short) or Touch (short)
// 2 Button (double) or Touch (double)
// 3 Button (mid) or Touch (mid)
// 4 Button (long) or Touch (long)
// 5 Touch1/2 (short)
// 6 Touch1/2 (double)
// 7 Touch1/2 (mid)
// 8 Touch1/2 (long)
/* Keypress => Sonde++ / Sonde-- / Display:=N*/
enum Events { EVT_NONE, EVT_KEY1SHORT, EVT_KEY1DOUBLE, EVT_KEY1MID, EVT_KEY1LONG,
EVT_KEY2SHORT, EVT_KEY2DOUBLE, EVT_KEY2MID, EVT_KEY2LONG,
EVT_VIEWTO, EVT_RXTO, EVT_NORXTO,
EVT_MAX };
extern const char *evstring[];
extern const char *RXstr[];
#define EVENTNAME(s) evstring[s]
//int8_t actions[EVT_MAX];
#define ACT_NONE 255
#define ACT_DISPLAY(n) (n)
#define ACT_MAXDISPLAY 50
#define ACT_DISPLAY_SCANNER 0
#define ACT_DISPLAY_NEXT 64
#define ACT_DISPLAY_DEFAULT 63
#define ACT_DISPLAY_SPECTRUM 62
#define ACT_DISPLAY_WIFI 61
#define ACT_NEXTSONDE 65
#define ACT_PREVSONDE 66
#define ACT_ADDFREQ(n) ((n)+64)
#define ACT_SONDE(n) ((n)+128)
// 0000nnnn => goto display nnnn
// 01000000 => goto sonde -1
// 01000001 => goto sonde +1
#define NSondeTypes 5
enum SondeType { STYPE_DFM06, STYPE_DFM09, STYPE_RS41, STYPE_RS92, STYPE_M10 };
extern const char *sondeTypeStr[NSondeTypes];
typedef struct st_sondeinfo {
// receiver configuration
bool active;
SondeType type;
float freq;
// decoded ID
char id[10];
char ser[12];
bool validID;
char launchsite[18];
// decoded position
float lat; // latitude
float lon; // longitude
float az; // azimut
float vbat; // vbat %
float alt; // altitude
float vs; // vertical speed
float hs; // horizontal speed
float dir; // 0..360
uint8_t sats; // number of sats
uint8_t validPos; // bit pattern for validity of above 7 fields; 0x80: position is old
// decoded GPS time
uint32_t time;
uint16_t sec;
uint32_t frame;
bool validTime;
// RSSI from receiver
int rssi; // signal strength
int32_t afc; // afc correction value
// statistics
uint8_t rxStat[20];
uint32_t rxStart; // millis() timestamp of continuous rx start
uint32_t norxStart; // millis() timestamp of continuous no rx start
uint32_t viewStart; // millis() timestamp of viewinf this sonde with current display
int8_t lastState; // -1: disabled; 0: norx; 1: rx
// shut down timers, currently only for RS41; -1=disabled
int16_t launchKT, burstKT, countKT;
uint16_t crefKT; // frame number in which countKT was last sent
} SondeInfo;
// rxStat: 3=undef[empty] 1=timeout[.] 2=errro[E] 0=ok[|] 4=no valid position[°]
// Used for interacting with the RX background task
typedef struct st_RXTask {
// Variables set by Arduino main loop to value >=0 for requesting
// mode change to sonde reception for sonde <value) in RXTask.
// Will be reset to -1 by RXTask
int activate;
// Variables set by RXTask, corresponding to mode ST_DECODER (if active) or something else,
// and currently received sonde
int mainState;
int currentSonde;
// Variable set by RXTask to communicate status to Arduino task
// via waitRXcomplete function
uint16_t receiveResult;
uint16_t receiveSonde; // sonde inde corresponding to receiveResult
// status variabe set by decoder to indicate something is broken
// int fifoOverflow;
} RXTask;
extern RXTask rxtask;
struct st_rs41config {
int agcbw;
int rxbw;
};
struct st_rs92config {
int rxbw;
int alt2d;
};
struct st_dfmconfig {
int agcbw;
int rxbw;
};
enum IDTYPE { ID_DFMDXL, ID_DFMGRAW, ID_DFMAUTO };
struct st_feedinfo {
bool active;
int type; // 0:UDP(axudp), 1:TCP(aprs.fi)
char host[64];
int port;
char symbol[3];
int lowrate;
int highrate;
int lowlimit;
int idformat; // 0: dxl 1: real 2: auto
};
// maybe extend for external Bluetooth interface?
// internal bluetooth consumes too much memory
struct st_kisstnc {
bool active;
int idformat;
};
typedef struct st_rdzconfig {
// hardware configuration
int button_pin; // PIN port number menu button (+128 for touch mode)
int button2_pin; // PIN port number menu button (+128 for touch mode)
int button2_axp; // Use AXP192 power button as button2
int touch_thresh; // Threshold value (0..100) for touch input button
int led_pout; // POUT port number of LED (used as serial monitor)
int power_pout; // Power control pin (for Heltec v2)
int disptype; // 0=OLED; 1=ILI9225
int oled_sda; // OLED data pin
int oled_scl; // OLED clock pin
int oled_rst; // OLED reset pin
int oled_orient; // OLED orientation (default: 1)
int gpsOn; // GPS Active On=1/Off=0
int gps_rxd; // GPS module RXD pin. We expect 9600 baud NMEA data.
int gps_txd; // GPS module TXD pin
char gps_lat[20]; // QTH no gps latitude
char gps_lon[20]; // QTH no gps longitude
int gps_alt; // QTH no gps altitude
// software configuration
int debug; // show port and config options after reboot
int wifi; // connect to known WLAN 0=skip
int wifiap; // enable/disable WiFi AccessPoint mode 0=disable
int8_t display[30]; // list of display mode (0:scanner, 1:default, 2,... additional modes)
int startfreq; // spectrum display start freq (400, 401, ...)
int channelbw; // spectrum channel bandwidth (valid: 5, 10, 20, 25, 50, 100 kHz)
int spectrum; // show freq spectrum for n seconds -1=disable; 0=forever
int marker; // show freq marker in spectrum 0=disable
int maxsonde; // number of max sonde in scan (range=1-99)
int norx_timeout; // Time after which rx mode switches to scan mode (without rx signal)
int noisefloor; // for spectrum display
char mdnsname[15]; // mDNS-Name, defaults to rdzsonde
char vbatmax[5]; // Vbat maxi when bat charged
char vbatmin[5]; // Vbat minimum discharged
int sdOn; // Active SDCard
int buzzerPort; // Buzzer port
int buzzerFreq; // Buzzer Frequency
int buzzerOn; // Buzzer On
int dbsmetre; // Db or Smetre display
// receiver configuration
int showafc; // show afc value in rx screen
int freqofs; // frequency offset (tuner config = rx frequency + freqofs) in Hz
struct st_rs41config rs41; // configuration options specific for RS41 receiver
struct st_rs92config rs92;
struct st_dfmconfig dfm;
// data feed configuration
// for now, one feed for each type is enough, but might get extended to more?
char call[10]; // APRS callsign
char passcode[9]; // APRS passcode
struct st_feedinfo udpfeed; // target for AXUDP messages
struct st_feedinfo tcpfeed; // target for APRS-IS TCP connections
struct st_kisstnc kisstnc; // target for KISS TNC (via TCP, mainly for APRSdroid)
} RDZConfig;
#define MAXSONDE 99
extern int fingerprintValue[];
extern const char *fingerprintText[];
class Sonde
{
private:
public:
RDZConfig config;
int fingerprint = 0;
int currentSonde = 0;
int nSonde;
String ipaddr;
bool isAP;
// moved to heap, saving space in .bss
//SondeInfo sondeList[MAXSONDE+1];
SondeInfo *sondeList;
Sonde();
void defaultConfig();
void setConfig(const char *str);
void clearSonde();
void addSonde(float frequency, SondeType type, int active, char *launchsite);
void nextConfig();
void nextRxSonde();
void nextRxFreq(int addkhz);
/* new interface */
void setup();
void receive();
uint16_t waitRXcomplete();
/* old and temp interface */
#if 0
void processRXbyte(uint8_t data);
int receiveFrame();
#endif
SondeInfo *si();
uint8_t timeoutEvent(SondeInfo *si);
uint8_t updateState(uint8_t event);
void updateDisplayPos();
void updateDisplayPos2();
void updateDisplayID();
void updateDisplayRSSI();
void updateDisplayRXConfig();
void updateStat();
void updateDisplayIP();
void updateDisplay();
void clearDisplay();
void setIP(String ip, bool isAP);
};
extern Sonde sonde;
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,468 @@
// SPDX-License-Identifier: GPL-3.0
// original source: https://github.com/Nkawu/TFT22_ILI9225
#ifndef TFT22_ILI9225_h
#define TFT22_ILI9225_h
#ifdef __STM32F1__
#define ARDUINO_STM32_FEATHER
#define PROGMEM
// if 'SPI_CHANNEL' is not defined, 'SPI' is used, only valid for STM32F1
//#define SPI_CHANNEL SPI_2
#endif
#define USE_STRING_CLASS
#ifdef USE_STRING_CLASS
#define STRING String
#else
#define STRING const char *
#endif
#if ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif
#include <SPI.h>
#include "gfxfont.h"
#if defined(ARDUINO_STM32_FEATHER) || defined(ARDUINO_ARCH_STM32) || defined(ARDUINO_ARCH_STM32F1) || defined(STM32F1)
typedef volatile uint32 RwReg;
#endif
#if defined(ARDUINO_FEATHER52)
typedef volatile uint32_t RwReg;
#endif
/* ILI9225 screen size */
#define ILI9225_LCD_WIDTH 176
#define ILI9225_LCD_HEIGHT 220
/* ILI9225 LCD Registers */
#define ILI9225_DRIVER_OUTPUT_CTRL (0x01u) // Driver Output Control
#define ILI9225_LCD_AC_DRIVING_CTRL (0x02u) // LCD AC Driving Control
#define ILI9225_ENTRY_MODE (0x03u) // Entry Mode
#define ILI9225_DISP_CTRL1 (0x07u) // Display Control 1
#define ILI9225_BLANK_PERIOD_CTRL1 (0x08u) // Blank Period Control
#define ILI9225_FRAME_CYCLE_CTRL (0x0Bu) // Frame Cycle Control
#define ILI9225_INTERFACE_CTRL (0x0Cu) // Interface Control
#define ILI9225_OSC_CTRL (0x0Fu) // Osc Control
#define ILI9225_POWER_CTRL1 (0x10u) // Power Control 1
#define ILI9225_POWER_CTRL2 (0x11u) // Power Control 2
#define ILI9225_POWER_CTRL3 (0x12u) // Power Control 3
#define ILI9225_POWER_CTRL4 (0x13u) // Power Control 4
#define ILI9225_POWER_CTRL5 (0x14u) // Power Control 5
#define ILI9225_VCI_RECYCLING (0x15u) // VCI Recycling
#define ILI9225_RAM_ADDR_SET1 (0x20u) // Horizontal GRAM Address Set
#define ILI9225_RAM_ADDR_SET2 (0x21u) // Vertical GRAM Address Set
#define ILI9225_GRAM_DATA_REG (0x22u) // GRAM Data Register
#define ILI9225_GATE_SCAN_CTRL (0x30u) // Gate Scan Control Register
#define ILI9225_VERTICAL_SCROLL_CTRL1 (0x31u) // Vertical Scroll Control 1 Register
#define ILI9225_VERTICAL_SCROLL_CTRL2 (0x32u) // Vertical Scroll Control 2 Register
#define ILI9225_VERTICAL_SCROLL_CTRL3 (0x33u) // Vertical Scroll Control 3 Register
#define ILI9225_PARTIAL_DRIVING_POS1 (0x34u) // Partial Driving Position 1 Register
#define ILI9225_PARTIAL_DRIVING_POS2 (0x35u) // Partial Driving Position 2 Register
#define ILI9225_HORIZONTAL_WINDOW_ADDR1 (0x36u) // Horizontal Address Start Position
#define ILI9225_HORIZONTAL_WINDOW_ADDR2 (0x37u) // Horizontal Address End Position
#define ILI9225_VERTICAL_WINDOW_ADDR1 (0x38u) // Vertical Address Start Position
#define ILI9225_VERTICAL_WINDOW_ADDR2 (0x39u) // Vertical Address End Position
#define ILI9225_GAMMA_CTRL1 (0x50u) // Gamma Control 1
#define ILI9225_GAMMA_CTRL2 (0x51u) // Gamma Control 2
#define ILI9225_GAMMA_CTRL3 (0x52u) // Gamma Control 3
#define ILI9225_GAMMA_CTRL4 (0x53u) // Gamma Control 4
#define ILI9225_GAMMA_CTRL5 (0x54u) // Gamma Control 5
#define ILI9225_GAMMA_CTRL6 (0x55u) // Gamma Control 6
#define ILI9225_GAMMA_CTRL7 (0x56u) // Gamma Control 7
#define ILI9225_GAMMA_CTRL8 (0x57u) // Gamma Control 8
#define ILI9225_GAMMA_CTRL9 (0x58u) // Gamma Control 9
#define ILI9225_GAMMA_CTRL10 (0x59u) // Gamma Control 10
#define ILI9225C_INVOFF 0x20
#define ILI9225C_INVON 0x21
// autoincrement modes (register ILI9225_ENTRY_MODE, bit 5..3 )
enum autoIncMode_t { R2L_BottomUp, BottomUp_R2L, L2R_BottomUp, BottomUp_L2R, R2L_TopDown, TopDown_R2L, L2R_TopDown, TopDown_L2R };
/* RGB 16-bit color table definition (RG565) */
#define COLOR_BLACK 0x0000 /* 0, 0, 0 */
#define COLOR_WHITE 0xFFFF /* 255, 255, 255 */
#define COLOR_BLUE 0x001F /* 0, 0, 255 */
#define COLOR_GREEN 0x07E0 /* 0, 255, 0 */
#define COLOR_RED 0xF800 /* 255, 0, 0 */
#define COLOR_NAVY 0x000F /* 0, 0, 128 */
#define COLOR_DARKBLUE 0x0011 /* 0, 0, 139 */
#define COLOR_DARKGREEN 0x03E0 /* 0, 128, 0 */
#define COLOR_DARKCYAN 0x03EF /* 0, 128, 128 */
#define COLOR_CYAN 0x07FF /* 0, 255, 255 */
#define COLOR_TURQUOISE 0x471A /* 64, 224, 208 */
#define COLOR_INDIGO 0x4810 /* 75, 0, 130 */
#define COLOR_DARKRED 0x8000 /* 128, 0, 0 */
#define COLOR_OLIVE 0x7BE0 /* 128, 128, 0 */
#define COLOR_GRAY 0x8410 /* 128, 128, 128 */
#define COLOR_GREY 0x8410 /* 128, 128, 128 */
#define COLOR_SKYBLUE 0x867D /* 135, 206, 235 */
#define COLOR_BLUEVIOLET 0x895C /* 138, 43, 226 */
#define COLOR_LIGHTGREEN 0x9772 /* 144, 238, 144 */
#define COLOR_DARKVIOLET 0x901A /* 148, 0, 211 */
#define COLOR_YELLOWGREEN 0x9E66 /* 154, 205, 50 */
#define COLOR_BROWN 0xA145 /* 165, 42, 42 */
#define COLOR_DARKGRAY 0x7BEF /* 128, 128, 128 */
#define COLOR_DARKGREY 0x7BEF /* 128, 128, 128 */
#define COLOR_SIENNA 0xA285 /* 160, 82, 45 */
#define COLOR_LIGHTBLUE 0xAEDC /* 172, 216, 230 */
#define COLOR_GREENYELLOW 0xAFE5 /* 173, 255, 47 */
#define COLOR_SILVER 0xC618 /* 192, 192, 192 */
#define COLOR_LIGHTGRAY 0xC618 /* 192, 192, 192 */
#define COLOR_LIGHTGREY 0xC618 /* 192, 192, 192 */
#define COLOR_LIGHTCYAN 0xE7FF /* 224, 255, 255 */
#define COLOR_VIOLET 0xEC1D /* 238, 130, 238 */
#define COLOR_AZUR 0xF7FF /* 240, 255, 255 */
#define COLOR_BEIGE 0xF7BB /* 245, 245, 220 */
#define COLOR_MAGENTA 0xF81F /* 255, 0, 255 */
#define COLOR_TOMATO 0xFB08 /* 255, 99, 71 */
#define COLOR_GOLD 0xFEA0 /* 255, 215, 0 */
#define COLOR_ORANGE 0xFD20 /* 255, 165, 0 */
#define COLOR_SNOW 0xFFDF /* 255, 250, 250 */
#define COLOR_YELLOW 0xFFE0 /* 255, 255, 0 */
/* Font defines */
#define FONT_HEADER_SIZE 4 // 1: pixel width of 1 font character, 2: pixel height,
#define readFontByte(x) pgm_read_byte(&cfont.font[x])
extern uint8_t Terminal6x8[];
extern uint8_t Terminal11x16[];
extern uint8_t Terminal12x16[];
extern uint8_t Trebuchet_MS16x21[];
struct _currentFont
{
uint8_t* font;
uint8_t width;
uint8_t height;
uint8_t offset;
uint8_t numchars;
uint8_t nbrows;
bool monoSp;
};
#define MONOSPACE 1
#if defined (ARDUINO_STM32_FEATHER)
#undef USE_FAST_PINIO
#elif defined (__AVR__) || defined(TEENSYDUINO) || defined(ESP8266) || defined(__arm__)
#define USE_FAST_PINIO
#endif
/// Main and core class
class TFT22_ILI9225 {
public:
TFT22_ILI9225(int8_t RST, int8_t RS, int8_t CS, int8_t SDI, int8_t CLK, int8_t LED);
TFT22_ILI9225(int8_t RST, int8_t RS, int8_t CS, int8_t LED);
TFT22_ILI9225(int8_t RST, int8_t RS, int8_t CS, int8_t SDI, int8_t CLK, int8_t LED, uint8_t brightness);
TFT22_ILI9225(int8_t RST, int8_t RS, int8_t CS, int8_t LED, uint8_t brightness);
/// Initialization
#ifndef ESP32
void begin(void);
#else
void begin(SPIClass &spi=SPI);
#endif
/// Clear the screen
void clear(void);
/// Invert screen
/// @param flag true to invert, false for normal screen
void invert(boolean flag);
/// Switch backlight on or off
/// @param flag true=on, false=off
void setBacklight(boolean flag);
/// Set backlight brightness
/// @param brightness sets backlight brightness 0-255
void setBacklightBrightness(uint8_t brightness);
/// Switch display on or off
/// @param flag true=on, false=off
void setDisplay(boolean flag);
/// Set orientation
/// @param orientation orientation, 0=portrait, 1=right rotated landscape, 2=reverse portrait, 3=left rotated landscape
void setOrientation(uint8_t orientation);
/// Get orientation
/// @return orientation orientation, 0=portrait, 1=right rotated landscape, 2=reverse portrait, 3=left rotated landscape
uint8_t getOrientation(void);
/// Font size, x-axis
/// @return horizontal size of current font, in pixels
// uint8_t fontX(void);
/// Font size, y-axis
/// @return vertical size of current font, in pixels
// uint8_t fontY(void);
/// Screen size, x-axis
/// @return horizontal size of the screen, in pixels
/// @note 240 means 240 pixels and thus 0..239 coordinates (decimal)
uint16_t maxX(void);
/// Screen size, y-axis
/// @return vertical size of the screen, in pixels
/// @note 220 means 220 pixels and thus 0..219 coordinates (decimal)
uint16_t maxY(void);
/// Draw circle
/// @param x0 center, point coordinate, x-axis
/// @param y0 center, point coordinate, y-axis
/// @param radius radius
/// @param color 16-bit color
void drawCircle(uint16_t x0, uint16_t y0, uint16_t radius, uint16_t color);
/// Draw solid circle
/// @param x0 center, point coordinate, x-axis
/// @param y0 center, point coordinate, y-axis
/// @param radius radius
/// @param color 16-bit color
void fillCircle(uint8_t x0, uint8_t y0, uint8_t radius, uint16_t color);
/// Set background color
/// @param color background color, default=black
void setBackgroundColor(uint16_t color = COLOR_BLACK);
/// Draw line, rectangle coordinates
/// @param x1 start point coordinate, x-axis
/// @param y1 start point coordinate, y-axis
/// @param x2 end point coordinate, x-axis
/// @param y2 end point coordinate, y-axis
/// @param color 16-bit color
void drawLine(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color);
/// Draw rectangle, rectangle coordinates
/// @param x1 top left coordinate, x-axis
/// @param y1 top left coordinate, y-axis
/// @param x2 bottom right coordinate, x-axis
/// @param y2 bottom right coordinate, y-axis
/// @param color 16-bit color
void drawRectangle(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color);
/// Draw solid rectangle, rectangle coordinates
/// @param x1 top left coordinate, x-axis
/// @param y1 top left coordinate, y-axis
/// @param x2 bottom right coordinate, x-axis
/// @param y2 bottom right coordinate, y-axis
/// @param color 16-bit color
void fillRectangle(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color);
/// Draw pixel
/// @param x1 point coordinate, x-axis
/// @param y1 point coordinate, y-axis
/// @param color 16-bit color
void drawPixel(uint16_t x1, uint16_t y1, uint16_t color);
/// Draw ASCII Text (pixel coordinates)
/// @param x point coordinate, x-axis
/// @param y point coordinate, y-axis
/// @param s text string
/// @param color 16-bit color, default=white
/// @return x-position behind text
uint16_t drawText(uint16_t x, uint16_t y, STRING s, uint16_t color = COLOR_WHITE);
/// width of an ASCII Text (pixel )
/// @param s text string
uint16_t getTextWidth( STRING s ) ;
/// Calculate 16-bit color from 8-bit Red-Green-Blue components
/// @param red red component, 0x00..0xff
/// @param green green component, 0x00..0xff
/// @param blue blue component, 0x00..0xff
/// @return 16-bit color
uint16_t setColor(uint8_t red, uint8_t green, uint8_t blue);
/// Calculate 8-bit Red-Green-Blue components from 16-bit color
/// @param rgb 16-bit color
/// @param red red component, 0x00..0xff
/// @param green green component, 0x00..0xff
/// @param blue blue component, 0x00..0xff
void splitColor(uint16_t rgb, uint8_t &red, uint8_t &green, uint8_t &blue);
/// Draw triangle, triangle coordinates
/// @param x1 corner 1 coordinate, x-axis
/// @param y1 corner 1 coordinate, y-axis
/// @param x2 corner 2 coordinate, x-axis
/// @param y2 corner 2 coordinate, y-axis
/// @param x3 corner 3 coordinate, x-axis
/// @param y3 corner 3 coordinate, y-axis
/// @param color 16-bit color
void drawTriangle(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t x3, uint16_t y3, uint16_t color);
/// Draw solid triangle, triangle coordinates
/// @param x1 corner 1 coordinate, x-axis
/// @param y1 corner 1 coordinate, y-axis
/// @param x2 corner 2 coordinate, x-axis
/// @param y2 corner 2 coordinate, y-axis
/// @param x3 corner 3 coordinate, x-axis
/// @param y3 corner 3 coordinate, y-axis
/// @param color 16-bit color
void fillTriangle(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t x3, uint16_t y3, uint16_t color);
/// Set current font
/// @param font Font name
void setFont(uint8_t* font, bool monoSp=false ); // default = proportional
/// Get current font
_currentFont getFont();
/// Draw single character (pixel coordinates)
/// @param x point coordinate, x-axis
/// @param y point coordinate, y-axis
/// @param ch ASCII character
/// @param color 16-bit color, default=white
/// @return width of character in display pixels
uint16_t drawChar(uint16_t x, uint16_t y, uint16_t ch, uint16_t color = COLOR_WHITE);
/// width of an ASCII character (pixel )
/// @param ch ASCII character
uint16_t getCharWidth( uint16_t ch ) ;
/// Draw bitmap
/// @param x point coordinate, x-axis
/// @param y point coordinate, y-axis
/// @param bitmap
/// @param w width
/// @param h height
/// @param color 16-bit color, default=white
/// @param bg 16-bit color, background
void drawBitmap(int16_t x, int16_t y, const uint8_t *bitmap, int16_t w, int16_t h, uint16_t color);
void drawBitmap(int16_t x, int16_t y, const uint8_t *bitmap, int16_t w, int16_t h, uint16_t color, uint16_t bg);
void drawBitmap(int16_t x, int16_t y, uint8_t *bitmap, int16_t w, int16_t h, uint16_t color);
void drawBitmap(int16_t x, int16_t y, uint8_t *bitmap, int16_t w, int16_t h, uint16_t color, uint16_t bg);
void drawXBitmap(int16_t x, int16_t y, const uint8_t *bitmap, int16_t w, int16_t h, uint16_t color);
void drawXBitmap(int16_t x, int16_t y, const uint8_t *bitmap, int16_t w, int16_t h, uint16_t color, uint16_t bg);
/// Draw bitmap
/// @param x point coordinate, x-axis
/// @param y point coordinate, y-axis
/// @param bitmap, 2D 16bit color bitmap
/// @param w width
/// @param h height
void drawBitmap(uint16_t x, uint16_t y, const uint16_t** bitmap, int16_t w, int16_t h);
void drawBitmap(uint16_t x, uint16_t y, uint16_t** bitmap, int16_t w, int16_t h);
/// Draw bitmap
/// @param x point coordinate, x-axis
/// @param y point coordinate, y-axis
/// @param bitmap, 1D 16bit color bitmap
/// @param w width
/// @param h height
void drawBitmap(uint16_t x, uint16_t y, const uint16_t* bitmap, int16_t w, int16_t h);
void drawBitmap(uint16_t x, uint16_t y, uint16_t* bitmap, int16_t w, int16_t h);
/// Set current GFX font
/// @param f GFX font name defined in include file
void setGFXFont(const GFXfont *f = NULL);
/// Draw a string with the current GFX font
/// @param x point coordinate, x-axis
/// @param y point coordinate, y-axis
/// @param s string to print
/// @param color 16-bit color
void drawGFXText(int16_t x, int16_t y, STRING s, uint16_t color);
/// Get the width & height of a text string with the current GFX font
/// @param str string to analyze
/// @param x point coordinate, x-axis
/// @param y point coordinate, y-axis
/// @param w width in pixels of string
/// @param h height in pixels of string
void getGFXTextExtent(STRING str, int16_t x, int16_t y, int16_t *w, int16_t *h);
/// Draw a single character with the current GFX font
/// @param x point coordinate, x-axis
/// @param y point coordinate, y-axis
/// @param c character to draw
/// @param color 16-bit color
/// @return width of character in display pixels
uint16_t drawGFXChar(int16_t x, int16_t y, unsigned char c, uint16_t color);
uint16_t drawGFXcharBM(int16_t x, int16_t y, unsigned char c, uint16_t color, uint16_t *bm, int bmwd);
void getGFXCharExtent(uint8_t c, int16_t *gw, int16_t *gh, int16_t *xa);
private:
void _spiWrite(uint8_t v);
void _spiWrite16(uint16_t v);
void _spiWriteCommand(uint8_t c);
void _spiWriteData(uint8_t d);
void _swap(uint16_t &a, uint16_t &b);
void _setWindow(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1);
void _setWindow(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, autoIncMode_t mode);
void _resetWindow();
void _drawBitmap(int16_t x, int16_t y, const uint8_t *bitmap, int16_t w, int16_t h,
uint16_t color, uint16_t bg, bool transparent, bool progmem, bool Xbit );
void _orientCoordinates(uint16_t &x1, uint16_t &y1);
void _writeRegister(uint16_t reg, uint16_t data);
void _writeData(uint8_t HI, uint8_t LO);
void _writeData16(uint16_t HILO);
void _writeCommand(uint8_t HI, uint8_t LO);
void _writeCommand16(uint16_t HILO);
uint16_t _maxX, _maxY, _bgColor;
#if defined (__AVR__) || defined(TEENSYDUINO)
int8_t _rst, _rs, _cs, _sdi, _clk, _led;
#ifdef USE_FAST_PINIO
volatile uint8_t *mosiport, *clkport, *dcport, *rsport, *csport;
uint8_t mosipinmask, clkpinmask, cspinmask, dcpinmask;
#endif
#elif defined (__arm__)
int32_t _rst, _rs, _cs, _sdi, _clk, _led;
#ifdef USE_FAST_PINIO
volatile RwReg *mosiport, *clkport, *dcport, *rsport, *csport;
uint32_t mosipinmask, clkpinmask, cspinmask, dcpinmask;
#endif
#elif defined (ESP8266) || defined (ESP32)
int8_t _rst, _rs, _cs, _sdi, _clk, _led;
#ifdef USE_FAST_PINIO
volatile uint32_t *mosiport, *clkport, *dcport, *rsport, *csport;
uint32_t mosipinmask, clkpinmask, cspinmask, dcpinmask;
#endif
#else
int8_t _rst, _rs, _cs, _sdi, _clk, _led;
#endif
uint8_t _orientation, _brightness;
// correspondig modes if orientation changed:
const autoIncMode_t modeTab [3][8] = {
// { R2L_BottomUp, BottomUp_R2L, L2R_BottomUp, BottomUp_L2R, R2L_TopDown, TopDown_R2L, L2R_TopDown, TopDown_L2R }//
/* 90° */ { BottomUp_L2R, L2R_BottomUp, TopDown_L2R, L2R_TopDown, BottomUp_R2L, R2L_BottomUp, TopDown_R2L, R2L_TopDown },
/*180° */ { L2R_TopDown , TopDown_L2R, R2L_TopDown, TopDown_R2L, L2R_BottomUp, BottomUp_L2R, R2L_BottomUp, BottomUp_R2L},
/*270° */ { TopDown_R2L , R2L_TopDown, BottomUp_R2L, R2L_BottomUp, TopDown_L2R, L2R_TopDown, BottomUp_L2R, L2R_BottomUp}
};
bool hwSPI, blState;
_currentFont cfont;
#ifdef ESP32
SPIClass _spi;
#endif
protected:
uint32_t writeFunctionLevel;
void startWrite(void);
void endWrite(void);
GFXfont *gfxFont;
};
#endif

350
libraries/SondeLib/aprs.cpp Executable file
View File

@ -0,0 +1,350 @@
/* Copyright (C) Hansi Reiser, dl9rdz
*
* partially based on dxlAPRS toolchain
*
* Copyright (C) Christian Rabler <oe5dxl@oevsv.at>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <stdio.h>
#include <WString.h>
#include <stdlib.h>
//#include <arpa/inet.h>
//#include <sys/socket.h>
#include <math.h>
#include <unistd.h>
#include <inttypes.h>
#include "aprs.h"
#if 0
int openudp(const char *ip, int port, struct sockaddr_in *si) {
int fd;
if((fd=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1) return -1;
memset((char *)&si, 0, sizeof(si));
si->sin_family = AF_INET;
si->sin_port = htons(port);
if(inet_aton(ip, &(si->sin_addr))==0) {
return -1;
}
return fd;
}
int sendudp(int fd, struct sockaddr_in *si, char *frame, int framelen)
{
if(sendto(fd, frame, framelen, 0, (struct sockaddr *)si, sizeof(struct sockaddr_in))==-1) {
return -1;
}
return 0;
}
#endif
void aprsstr_append(char *b, const char *data)
{
int blen=strlen(b);
int len=strlen(data);
if(blen+len>APRS_MAXLEN) len=APRS_MAXLEN-blen;
strncat(b, data, len);
}
uint32_t realcard(float x) {
if(x<0) return 0;
else return (uint32_t)x;
}
/* CRC for AXUDP frames */
#define APRSCRC_POLY 0x8408
static uint8_t CRCL[256];
static uint8_t CRCH[256];
void aprs_gencrctab(void)
{
uint32_t c;
uint32_t crc;
uint32_t i;
for (c = 0UL; c<=255UL; c++) {
crc = 255UL-c;
for (i = 0UL; i<=7UL; i++) {
if ((crc&1)) crc = (uint32_t)((uint32_t)(crc>>1)^APRSCRC_POLY);
else crc = crc>>1;
} /* end for */
CRCL[c] = (uint8_t)crc;
CRCH[c] = (uint8_t)(255UL-(crc>>8));
} /* end for */
} /* end Gencrctab() */
static void aprsstr_appcrc(char frame[], uint32_t frame_len, int32_t size)
{
uint8_t h;
uint8_t l;
uint8_t b;
int32_t i;
int32_t tmp;
l = 0U;
h = 0U;
tmp = size-1L;
i = 0L;
if (i<=tmp) for (;; i++) {
b = (uint8_t)((uint8_t)(uint8_t)frame[i]^l);
l = CRCL[b]^h;
h = CRCH[b];
if (i==tmp) break;
} /* end for */
frame[size] = (char)l;
frame[size+1L] = (char)h;
} /* end aprsstr_appcrc() */
static int mkaprscall(int32_t * p, char raw[],
uint32_t * i, const char mon[],
char sep1, char sep2, char sep3,
uint32_t sbase)
{
uint32_t s;
uint32_t l;
l = 0UL;
while ((((mon[*i] && mon[*i]!=sep1) && mon[*i]!=sep2) && mon[*i]!=sep3)
&& mon[*i]!='-') {
s = (uint32_t)(uint8_t)mon[*i]*2UL&255UL;
if (s<=64UL) return 0;
raw[*p] = (char)s;
++*p;
++*i;
++l;
if (l>=7UL) return 0;
}
while (l<6UL) {
raw[*p] = '@';
++*p;
++l;
}
s = 0UL;
if (mon[*i]=='-') {
++*i;
while ((uint8_t)mon[*i]>='0' && (uint8_t)mon[*i]<='9') {
s = (s*10UL+(uint32_t)(uint8_t)mon[*i])-48UL;
++*i;
}
if (s>15UL) return 0;
}
raw[*p] = (char)((s+sbase)*2UL);
++*p;
return 1;
} /* end call() */
// returns raw len, 0 in case of error
extern int aprsstr_mon2raw(const char *mon, char raw[], int raw_len)
{
uint32_t r;
uint32_t n;
uint32_t i;
uint32_t tmp;
int p = 7L;
i = 0UL;
fprintf(stderr,"mon2raw for %s\n", mon);
if (!mkaprscall(&p, raw, &i, mon, '>', 0, 0, 48UL)) {
return 0;
}
p = 0L;
if (mon[i]!='>') return 0;
/* ">" */
++i;
if (!mkaprscall(&p, raw, &i, mon, ':', ',', 0, 112UL)) {
return 0;
}
p = 14L;
n = 0UL;
while (mon[i]==',') {
++i;
if (!mkaprscall(&p, raw, &i, mon, ':', ',', '*', 48UL)) {
return 0;
}
++n;
if (n>8UL) {
return 0;
}
if (mon[i]=='*') {
/* "*" has repeatet sign */
++i;
r = (uint32_t)p;
if (r>=21UL) for (tmp = (uint32_t)(r-21UL)/7UL;;) {
raw[r-1UL] = (char)((uint32_t)(uint8_t)raw[r-1UL]+128UL);
/* set "has repeated" flags */
if (!tmp) break;
--tmp;
r -= 7UL;
} /* end for */
}
}
if (p==0L || mon[i]!=':') {
return 0;
}
raw[p-1L] = (char)((uint32_t)(uint8_t)raw[p-1L]+1UL);
/* end address field mark */
raw[p] = '\003';
++p;
raw[p] = '\360';
++p;
++i;
n = 256UL;
while (mon[i]) {
/* copy info part */
if (p>=(int32_t)(raw_len-1)-2L || n==0UL) {
return 0;
}
raw[p] = mon[i];
++p;
++i;
--n;
}
aprsstr_appcrc(raw, raw_len, p);
fprintf(stderr,"results in %s\n",raw);
return p+2;
} /* end mon2raw() */
extern int aprsstr_mon2kiss(const char *mon, char raw[], int raw_len)
{
char tmp[201];
int len = aprsstr_mon2raw(mon, tmp, 201);
if(len==0) return 0;
int idx=0;
raw[idx++] = '\xC0';
for(int i=0; i<len-2; i++) { // -2: discard CRC, not used in KISS
if(tmp[i]=='\xC0') {
raw[idx++] = '\xDB';
raw[idx++] = '\xDC';
} else if (tmp[i]=='\xDB') {
raw[idx++] = '\xDB';
raw[idx++] = '\xDD';
} else {
raw[idx++] = tmp[i];
}
if(idx>=raw_len)
return 0;
}
return idx;
}
#define FEET (1.0/0.3048)
#define KNOTS (1.851984)
#define X2C_max_longcard 0xFFFFFFFFUL
static uint32_t X2C_TRUNCC(double x, uint32_t min0, uint32_t max0)
{
uint32_t i;
if (x < (double)min0)
i = (uint32_t)min0;
if (x > (double)max0)
i = (uint32_t)max0;
i = (uint32_t)x;
if ((double)i > x)
--i;
return i;
}
static uint32_t truncc(double r)
{
if (r<=0.0) return 0UL;
else if (r>=2.E+9) return 2000000000UL;
else return (uint32_t)X2C_TRUNCC(r,0UL,X2C_max_longcard);
return 0;
} /* end truncc() */
static uint32_t dao91(double x)
/* radix91(xx/1.1) of dddmm.mmxx */
{
double a;
a = fabs(x);
return ((truncc((a-(double)(float)truncc(a))*6.E+5)%100UL)
*20UL+11UL)/22UL;
} /* end dao91() */
char b[201];
char raw[201];
char *aprs_senddata(SondeInfo *s, const char *usercall, const char *sym) {
// float lat, float lon, float alt, float speed, float dir, float climb, const char *type, const char *objname, const char *usercall, const char *sym, const char *comm)
*b=0;
aprsstr_append(b, usercall);
aprsstr_append(b, ">");
const char *destcall="APZRDZ";
aprsstr_append(b, destcall);
// uncompressed
aprsstr_append(b, ":;");
char tmp[10];
snprintf(tmp,10,"%s ",s->id);
aprsstr_append(b, tmp);
aprsstr_append(b, "*");
// time
int i = strlen(b);
int sec = s->time % 86400;
snprintf(b+i, APRS_MAXLEN-1, "%02d%02d%02dz", sec/(60*60), (sec%(60*60))/60, sec%60);
i = strlen(b);
//aprsstr_append_data(time, ds);
int lati = abs((int)s->lat);
int latm = (fabs(s->lat)-lati)*6000;
snprintf(b+i, APRS_MAXLEN-i, "%02d%02d.%02d%c%c", lati, latm/100, latm%100, s->lat<0?'S':'N', sym[0]);
i = strlen(b);
int loni = abs((int)s->lon);
int lonm = (fabs(s->lon)-loni)*6000;
snprintf(b+i, APRS_MAXLEN-i, "%03d%02d.%02d%c%c", loni, lonm/100, lonm%100, s->lon<0?'W':'E', sym[1]);
if(s->hs>0.5) {
i=strlen(b);
snprintf(b+i, APRS_MAXLEN-i, "%03d/%03d", realcard(s->dir+1.5), realcard(s->hs*1.0/KNOTS+0.5));
}
if(s->alt>0.5) {
i=strlen(b);
snprintf(b+i, APRS_MAXLEN-i, "/A=%06d", realcard(s->alt*FEET+0.5));
}
int dao=1;
if(dao) {
i=strlen(b);
snprintf(b+i, APRS_MAXLEN-i, "!w%c%c!", 33+dao91(s->lat), 33+dao91(s->lon));
}
strcat(b, "&");
char comm[100];
snprintf(comm, 100, "Clb=%.1fm/s %.3fMHz Type=%s", s->vs, s->freq, sondeTypeStr[s->type]);
strcat(b, comm);
if(s->type==STYPE_M10||s->type==STYPE_DFM06||s->type==STYPE_DFM09) {
snprintf(comm, 100, " ser=%s", s->ser);
strcat(b, comm);
}
return b;
}
#if 0
int main(int argc, char *argv[])
{
Gencrctab();
struct sockaddr_in si;
int fd = openudp("127.0.0.1",9002,&si);
if(fd<0) { fprintf(stderr,"open failed\n"); return 1; }
float lat=48, lon=10;
while(1) {
const char *str = aprs_senddata(lat, lon, 543, 5, 180, 1.5, "RS41", "TE0ST", "TE1ST", "EO");
int rawlen = aprsstr_mon2raw(str, raw, APRS_MAXLEN);
sendudp(fd, raw, rawlen);
str = "OE3XKC>APMI06,qAR,OE3XLR:;ER-341109*111111z4803.61NE01532.39E0145.650MHz R15k OE3XPA";
rawlen = aprsstr_mon2raw(str, raw, APRS_MAXLEN);
sendudp(fd, &si, raw, rawlen);
lat += 0.002; lon += 0.01;
sleep(5);
}
}
#endif

14
libraries/SondeLib/aprs.h Executable file
View File

@ -0,0 +1,14 @@
#ifndef _aprs_h
#define _aprs_h
#include "Sonde.h"
#define APRS_MAXLEN 201
void aprs_gencrctab(void);
int aprsstr_mon2raw(const char *mon, char raw[], int raw_len);
int aprsstr_mon2kiss(const char *mon, char raw[], int raw_len);
char *aprs_senddata(SondeInfo *s, const char *usercall, const char *sym);
#endif

View File

@ -0,0 +1,68 @@
Heltec board v1 => fingerprint 0000100 => 4
(sda,scl: 4,15) (same as LORA v1.0)
0:1 1:0 2:0 3:1 4:0 5:1 6:0 7:1 8:0 9:1 10:1 11:1 12:0 13:0 14:1 15:0 16:0 17:0 18:0 19:0 20:0 21:1 22:0 23:0 24:0 25:0 26:0 27:0 28:0 29:0 30:0 31:0 32:0 33:0 34:0 35:0 36:0 37:0 38:0
Heltec board v2
(sda,scl: 4,15) (similar to v1.0, but GPIO21 switches 3V3) => fingerprint 4
0:1 1:0 2:0 3:1 4:0 5:1 6:0 7:1 8:0 9:1 10:1 11:1 12:0 13:0 14:1 15:0 16:0 17:0 18:0 19:0 20:0 21:1 22:0 23:0 24:0 25:0 26:0 27:0 28:0 29:0 30:0 31:0 32:0 33:0 34:0 35:0 36:0 37:0 38:0
0:1 1:1 2:0 3:1 4:0 5:1 6:0 7:1 8:0 9:1 10:1 11:1 12:0 13:0 14:1 15:0 16:0 17:0 18:0 19:0 20:0 21:1 22:0 23:0 24:0 25:0 26:0 27:0 28:0 29:0 30:0 31:0 32:0 33:0 34:0 35:0 36:0 37:0 38:0 (before setup
TTGO LORA32 v2.1_1.6 (button1: touch gpio 2 => 130; button2: touch gpio14 => 142) fingerprint 0011111 => 31
(sda,scl: 21,22)
0:1 1:0 2:0 3:1 4:0 5:1 6:0 7:1 8:0 9:1 10:1 11:1 12:0 13:0 14:1 15:1 16:1 17:1 18:0 19:0 20:0 21:1 22:1 23:1 24:0 25:0 26:0 27:0 28:0 29:0 30:0 31:0 32:0 33:0 34:0 35:0 36:0 37:0 38:0
0:4 1:4 2:0 3:4 4:0 5:4 6:0 7:4 8:0 9:4 10:4 11:4 12:0 13:0 14:4 15:4 16:4 17:4 18:0 19:0 20:0 21:4 22:4 23:4 24:0 25:0 26:0 27:0 28:0 29:0 30:0 31:0 32:0 33:0 34:0 35:0 36:0 37:0 38:0 (in Sonde())
TTGO LORA v1.0 => fingerprint 1000000 => 64
(sda,scl: 4,15) (button1: 0) (button2: touch gpio13 = 141)
0:1 1:0 2:0 3:1 4:1 5:1 6:0 7:1 8:0 9:1 10:1 11:1 12:0 13:0 14:1 15:1 16:0 17:0 18:0 19:0 20:0 21:0 22:0 23:0 24:0 25:0 26:0 27:0 28:0 29:0 30:0 31:0 32:0 33:0 34:0 35:0 36:0 37:0 38:0
0:4 1:4 2:0 3:4 4:4 5:4 6:0 7:4 8:0 9:4 10:4 11:4 12:0 13:0 14:4 15:4 16:0 17:0 18:0 19:0 20:0 21:0 22:0 23:0 24:0 25:0 26:0 27:0 28:0 29:0 30:0 31:0 32:0 33:0 34:0 35:0 36:0 37:0 38:0 (in Sonde())
TTGO T-Beam => fingerprint 0110111 => 55
(sda,scl: 21,22) (button1: 39) (button2: touch gpio13 = 141) (gps rx: 12)
0:1 1:0 2:0 3:1 4:0 5:1 6:0 7:1 8:0 9:1 10:1 11:1 12:1 13:0 14:1 15:1 16:1 17:0 18:0 19:0 20:0 21:1 22:1 23:1 24:0 25:0 26:0 27:0 28:0 29:0 30:0 31:0 32:0 33:0 34:0 35:0 36:0 37:0 38:0
0:4 1:4 2:0 3:4 4:0 5:4 6:0 7:4 8:0 9:4 10:4 11:4 12:4 13:0 14:4 15:4 16:4 17:0 18:0 19:0 20:0 21:4 22:4 23:4 24:0 25:0 26:0 27:0 28:0 29:0 30:0 31:0 32:0 33:0 34:0 35:0 36:0 37:0 38:0 (before setup)
TTGO T-Beam with extern 2" ILI9225 Display => fingerprint 0110000 => 48
0:1 1:0 2:0 3:1 4:0 5:1 6:0 7:1 8:0 9:1 10:1 11:1 12:1 13:0 14:1 15:1 16:1 17:0 18:0 19:0 20:0 21:0 22:0 23:1 24:0 25:0 26:0 27:0 28:0 29:0 30:0 31:0 32:0 33:0 34:0 35:0 36:0 37:0 38:0
0:4 1:4 2:0 3:4 4:0 5:4 6:0 7:4 8:0 9:4 10:4 11:4 12:4 13:0 14:4 15:4 16:4 17:0 18:0 19:0 20:0 21:0 22:0 23:4 24:0 25:0 26:0 27:0 28:0 29:0 30:0 31:0 32:0 33:0 34:0 35:0 36:0 37:0 38:0 (before setup)
TTGO T-Beam 1.0 with OLED display => fingerprint 0010111 => 23
0:1 1:0 2:0 3:1 4:0 5:1 6:0 7:1 8:0 9:1 10:1 11:1 12:0 13:0 14:1 15:1 16:1 17:0 18:0 19:0 20:0 21:1 22:1 23:1 24:0 25:0 26:0 27:0 28:0 29:0 30:0 31:0 32:0 33:0 34:0 35:0 36:0 37:0 38:0
0:4 1:4 2:0 3:4 4:0 5:4 6:0 7:4 8:0 9:4 10:4 11:4 12:0 13:0 14:4 15:4 16:4 17:0 18:0 19:0 20:0 21:4 22:4 23:4 24:0 25:0 26:0 27:0 28:0 29:0 30:0 31:0 32:0 33:0 34:0 35:0 36:0 37:0 38:0 (before setup)
1
Fingerprint GPIOs: 4, 12, 16, 17, 21, 22, 23,
Current autodetect strategy:
RST always set to 16
GPIO16=0 (GPio22,23 would also work):
==Heltec or TTGO LORA v1.0==
SDA,SCL set to (4,15)
Button 1 set to GPIO 0
Button 2 set to Touch in GPIO 13 (141)
otherwise
==LORA32 v2.1 or T-Beam==
GPIO17=0:
== T-BEAM =
GPIO12==0: v1 (or check PMU via I2C?)
GPS RX set to 34
Button 1 set to GPIO28
BUtton 2 set to Touch GPIO13
else:
GPS RX set to 12
Button 1 set to GPIO39
Button 2 set to Touch GPIO13 (141)
GPIO21=0:
large display connected (use ILI9225 contig: SDA4 CLK21 RS2 RST22 CS0)
else:
small display connected, set SDA,SCL to (21,22)
otherweise:
SDA,SCL set to (21,22)
GPS disabled
Button 1 set to Touch GPIO2 (130)
Button 2 set to Touch GPIO14 (142)

226
libraries/SondeLib/geteph.cpp Executable file
View File

@ -0,0 +1,226 @@
#include "time.h"
#include "geteph.h"
#include <SPIFFS.h>
#include <WiFi.h>
#include <rom/miniz.h>
#include <inttypes.h>
#include <WiFi.h>
#include "Display.h"
extern WiFiClient client;
static const char *ftpserver = "www.ngs.noaa.gov";
char outbuf[128];
uint8_t getreply() {
String s = client.readStringUntil('\n');
Serial.println(s);
const char *str = s.c_str();
if(strlen(str)<4) return 255; // something unusual...
if(str[3]=='-') { // multi-line resonse...
String s2;
const char *str2;
do {
s2 = client.readStringUntil('\n');
Serial.println(s2);
str2 = s2.c_str();
if(strlen(str2)<4) return 255; // something is wrong
} while( str[0]!=str2[0] || str[1]!=str2[1] || str[2]!=str2[2] || str2[3]!=' ' );
}
return str[0];
}
void writeFully(File &file, uint8_t *buf, size_t len)
{
size_t olen;
while(len) {
olen = file.write(buf, len);
Serial.printf("written: %d of %d\n", olen, len);
len -= olen;
buf += olen;
}
}
void geteph() {
// Set current time via network...
struct tm tinfo;
configTime(0, 0, "pool.ntp.org");
bool ok = getLocalTime(&tinfo, 2000); // wait max 2 seconds to get current time via ntp
if(!ok) {
Serial.println("Failed to get current date/time");
return;
}
// Check time of last update
int year = tinfo.tm_year + 1900;
int day = tinfo.tm_yday + 1;
Serial.printf("year %d, day %d\n", year, day);
char nowstr[20];
snprintf(nowstr, 20, "%04d%03d%02d", year, day, tinfo.tm_hour);
File status = SPIFFS.open("/brdc.time", "r");
if(status) {
String ts = status.readStringUntil('\n');
const char *tsstr = ts.c_str();
if(tsstr && strlen(tsstr)>=9) {
if(strcmp(nowstr, ts.c_str())<=0) {
Serial.println("local brdc is up to date\n");
return;
}
}
Serial.printf("now: %s, existing: %s => updating\n", nowstr, tsstr);
}
status.close();
disp.rdis->clear();
disp.rdis->setFont(FONT_SMALL);
disp.rdis->drawString(0, 0, "FTP ngs.noaa.gov");
// fetch rinex from server
File fh = SPIFFS.open("/brdc.gz","w");
if(!fh) {
Serial.println("cannot open file\n");
return;
}
char buf[252];
snprintf(buf, 128, "/cors/rinex/%04d/%03d/brdc%03d0.%02dn.gz", year, day, day, year-2000);
Serial.println("running geteph\n");
disp.rdis->drawString(0, 1, buf+21);
if(!client.connect(ftpserver, 21)) {
Serial.println("FTP connection to www.ngs.noaa.gov failed");
return;
}
#if 0
while(!client.available()) delay(1);
while(client.available()) {
String s = client.readStringUntil('\n');
Serial.println(s);
}
#endif
if(getreply()>='4') { Serial.println("connected failed"); return; }
client.print("USER anonymous\r\n");
if(getreply()>='4') { Serial.println("USER failed"); return; }
client.print("PASS anonymous\r\n");
if(getreply()>='4') { Serial.println("PASS failed"); return; }
client.print("TYPE I\r\n");
if(getreply()>='4') { Serial.println("TYPE I failed"); return; }
client.print("PASV\r\n");
String s = client.readStringUntil('\n');
Serial.println(s);
if(s.c_str()[0]>='4') { Serial.println("PASV failed"); return; }
int array_pasv[6];
char *tStr = strtok((char *)s.c_str(), "(,");
for(int i=0; i<6; i++) {
tStr = strtok(NULL, "(,");
if(tStr==NULL) {
Serial.println("strange response to PASV");
return;
}
array_pasv[i] = atoi(tStr);
Serial.println(array_pasv[i]);
}
uint16_t port = (array_pasv[4]<<8) | (array_pasv[5]&0xff);
WiFiClient dclient;
Serial.printf("connecting to %s:%d\n", ftpserver,port);
dclient.connect(ftpserver, port);
if(!dclient) {
Serial.println("data connection failed");
return;
}
client.print("RETR ");
Serial.printf("fetching %s with FTP...\n", buf);
client.println(buf);
s = client.readStringUntil('\n');
Serial.println(s);
if(s.c_str()[0]>='4') { Serial.println("RETR failed"); return; }
int len=0;
while(dclient.connected()) {
while(dclient.available()) {
char c = dclient.read();
fh.write(c);
len++;
}
}
Serial.printf("fetched %d bytes\n", len);
fh.close();
snprintf(buf, 16, "Fetched %d B ",len);
buf[16]=0;
disp.rdis->drawString(0,2,buf);
disp.rdis->drawString(0,4,"Decompressing...");
// decompression
tinfl_decompressor *decomp = (tinfl_decompressor *)malloc(sizeof(tinfl_decompressor));
tinfl_init(decomp);
File file = SPIFFS.open("/brdc.gz","r");
if(!file) {
Serial.println("cannot open file\n");
return;
}
File ofile = SPIFFS.open("/brdc", "w");
if(!ofile) {
Serial.println("cannot open file /brdc for writing");
return;
}
file.readBytes(buf, 10); // skip gzip header
char flags = buf[3];
if(flags&0x07) {
Serial.println("Unsupported flags in gzip header, may or may not cause a problem");
}
if(flags&0x08) { // skip file name extra header
do {
int res=file.readBytes(buf, 1);
if(res!=1) return;
} while(*buf);
}
if(flags&0x10) { // skip file name extra header
do {
int res=file.readBytes(buf, 1);
if(res!=1) return;
} while(*buf);
}
int opos = 0;
int total = 0;
Serial.println("Decompressing ephemeris data...\n");
char *obuf =(char *)malloc(32768);
char *ibuf =(char *)malloc(8192);
while(file.available()) {
size_t len = file.readBytes(ibuf, 8192);
size_t inofs = 0;
size_t inlen = len;
while(inofs<len) {
size_t outlen=32768-opos;
int res = tinfl_decompress(decomp, (const mz_uint8 *)ibuf+inofs, &inlen, (uint8_t *)obuf, (mz_uint8 *)obuf+opos, &outlen, TINFL_FLAG_HAS_MORE_INPUT);
if(res<0) break;
if(outlen==0) break;
Serial.printf("... (res=%d) decompressed %d into %d bytes\n", res, inlen, outlen);
inofs += inlen;
inlen = len - inofs;
//size_t retv = ofile.write((uint8_t *)(obuf+opos), outlen);
//Serial.printf("write %d bytes\n", retv);
writeFully(ofile, (uint8_t *)(obuf+opos), outlen);
//Serial.write((uint8_t *)(obuf+opos), outlen);
total += outlen;
opos += outlen;
if(res==0) break; // done indication
if(opos>=32768) {
Serial.printf("... decompressed %d bytes\n", total);
opos=0;
}
}
}
// maybe todo: check crc?!?
Serial.printf("done extracing content (total length: %d)\n", total);
status = SPIFFS.open("/brdc.time","w");
status.println(nowstr);
status.close();
snprintf(buf, 16, "Done: %d B ",total);
buf[16]=0;
disp.rdis->drawString(0,5,buf);
delay(1000);
free(obuf);
free(ibuf);
free(decomp);
file.close();
ofile.close();
}

3
libraries/SondeLib/geteph.h Executable file
View File

@ -0,0 +1,3 @@
void geteph();

26
libraries/SondeLib/gfxfont.h Executable file
View File

@ -0,0 +1,26 @@
// SPDX-License-Identifier: GPL-3.0
// original source: https://github.com/Nkawu/TFT_22_ILI9225
// Font structures like Adafruit_GFX (1.1 and later).
// Example fonts are included in 'fonts' directory.
// To use a font in your Arduino sketch, #include the corresponding .h
// file and pass address of GFXfont struct to setFont().
#ifndef _GFFFONT_H_
#define _GFFFONT_H_
typedef struct { // Data stored PER GLYPH
uint16_t bitmapOffset; // Pointer into GFXfont->bitmap
uint8_t width, height; // Bitmap dimensions in pixels
uint8_t xAdvance; // Distance to advance cursor (x axis)
int8_t xOffset, yOffset; // Dist from cursor pos to UL corner
} GFXglyph;
typedef struct { // Data stored for FONT AS A WHOLE:
uint8_t *bitmap; // Glyph bitmaps, concatenated
GFXglyph *glyph; // Glyph array
uint8_t first, last; // ASCII extents
uint8_t yAdvance; // Newline distance (y axis)
} GFXfont;
#endif // _GFFFONT_H_

1866
libraries/SondeLib/nav_gps_vel.cpp Executable file

File diff suppressed because it is too large Load Diff

148
libraries/SondeLib/nav_gps_vel.h Executable file
View File

@ -0,0 +1,148 @@
typedef struct {
uint16_t prn;
uint16_t week;
uint32_t toa;
char epoch[20];
double toe;
double toc;
double e;
double delta_n;
double delta_i;
double i0;
double OmegaDot;
double sqrta;
double Omega0;
double w;
double M0;
double tgd;
double idot;
double cuc;
double cus;
double crc;
double crs;
double cic;
double cis;
double af0;
double af1;
double af2;
int gpsweek;
uint16_t svn;
uint8_t ura;
uint8_t health;
uint8_t conf;
} EPHEM_t;
typedef struct {
uint32_t t;
double pseudorange;
double pseudorate;
double clock_corr;
double clock_drift;
double X;
double Y;
double Z;
double vX;
double vY;
double vZ;
int ephhr;
double PR;
double ephtime;
int prn;
} SAT_t;
typedef struct {double X; double Y; double Z;} LOC_t;
typedef struct {double X; double Y; double Z;
double vX; double vY; double vZ;} VEL_t;
double dist(double X1, double Y1, double Z1, double X2, double Y2, double Z2);
void GPS_SatelliteClockCorrection(
const unsigned short transmission_gpsweek, // GPS week when signal was transmit (0-1024+) [weeks]
const double transmission_gpstow, // GPS time of week when signal was transmit [s]
const unsigned short ephem_week, // ephemeris: GPS week (0-1024+) [weeks]
const double toe, // ephemeris: time of week [s]
const double toc, // ephemeris: clock reference time of week [s]
const double af0, // ephemeris: polynomial clock correction coefficient [s],
const double af1, // ephemeris: polynomial clock correction coefficient [s/s],
const double af2, // ephemeris: polynomial clock correction coefficient [s/s^2]
const double ecc, // ephemeris: eccentricity of satellite orbit []
const double sqrta, // ephemeris: square root of the semi-major axis of orbit [m^(1/2)]
const double delta_n, // ephemeris: mean motion difference from computed value [rad]
const double m0, // ephemeris: mean anomaly at reference time [rad]
const double tgd, // ephemeris: group delay differential between L1 and L2 [s]
double* clock_correction );
void GPS_SatellitePosition_Ephem(
const unsigned short gpsweek, // gps week of signal transmission (0-1024+) [week]
const double gpstow, // time of week of signal transmission (gpstow-psr/c) [s]
EPHEM_t ephem,
double* clock_correction, // clock correction for this satellite for this epoch [m]
double* satX, // satellite X position WGS84 ECEF [m]
double* satY, // satellite Y position WGS84 ECEF [m]
double* satZ // satellite Z position WGS84 ECEF [m]
);
void GPS_SatelliteClockDriftCorrection(
const unsigned short transmission_gpsweek, // GPS week when signal was transmit (0-1024+) [weeks]
const double transmission_gpstow, // GPS time of week when signal was transmit [s]
const unsigned short ephem_week, // ephemeris: GPS week (0-1024+) [weeks]
const double toe, // ephemeris: time of week [s]
const double toc, // ephemeris: clock reference time of week [s]
const double af0, // ephemeris: polynomial clock correction coefficient [s],
const double af1, // ephemeris: polynomial clock correction coefficient [s/s],
const double af2, // ephemeris: polynomial clock correction coefficient [s/s^2]
const double ecc, // ephemeris: eccentricity of satellite orbit []
const double sqrta, // ephemeris: square root of the semi-major axis of orbit [m^(1/2)]
const double delta_n, // ephemeris: mean motion difference from computed value [rad]
const double m0, // ephemeris: mean anomaly at reference time [rad]
const double tgd, // ephemeris: group delay differential between L1 and L2 [s]
double* clock_correction, // ephemeris: satellite clock correction [m]
double* clock_drift ) ;
void GPS_SatellitePositionVelocity_Ephem(
const unsigned short gpsweek, // gps week of signal transmission (0-1024+) [week]
const double gpstow, // time of week of signal transmission (gpstow-psr/c) [s]
EPHEM_t ephem,
double* clock_correction, // clock correction for this satellite for this epoch [m]
double* clock_drift, // clock correction for this satellite for this epoch [m]
double* satX, // satellite X position WGS84 ECEF [m]
double* satY, // satellite Y position WGS84 ECEF [m]
double* satZ, // satellite Z position WGS84 ECEF [m]
double* satvX, // satellite X velocity WGS84 ECEF [m]
double* satvY, // satellite Y velocity WGS84 ECEF [m]
double* satvZ // satellite Z velocity WGS84 ECEF [m]
);
int NAV_ClosedFormSolution_FromPseudorange(
SAT_t sats[4], // input: satellite position and pseudorange
double* latitude, // output: ellipsoid latitude [rad]
double* longitude, // ellipsoid longitude [rad]
double* height, // ellipsoid height [m]
double* rx_clock_bias, // receiver clock bias [m]
double pos_ecef[3] );
int calc_DOPn(int n, SAT_t satss[], double pos_ecef[3], double DOP[4]);
int NAV_LinP(int N, SAT_t satv[], double pos_ecef[3], double dt,
double dpos_ecef[3], double *cc);
void ecef2elli(double X, double Y, double Z, double *lat, double *lon, double *alt);
int NAV_LinP(int N, SAT_t satv[], double pos_ecef[3], double dt,
double dpos_ecef[3], double *cc);
int NAV_LinV(int N, SAT_t satv[], double pos_ecef[3],
double vel_ecef[3], double dt,
double dvel_ecef[3], double *cc);
int NAV_bancroft1(int N, SAT_t sats[], double pos_ecef[3], double *cc);
EPHEM_t *read_RNXpephs(const char *file);

1735
libraries/SondeLib/rs92gps.cpp Executable file

File diff suppressed because it is too large Load Diff

22
libraries/SondeLib/rs92gps.h Executable file
View File

@ -0,0 +1,22 @@
typedef struct {
int frnr;
char id[11];
int week; int gpssec;
int jahr; int monat; int tag;
int wday;
int std; int min; float sek;
double lat; double lon; double alt;
double vH; double vD; double vU;
int k;
int sats[4];
double dop;
int freq;
unsigned short aux[4];
double diter;
} gpx_t;
extern gpx_t gpx;
void print_frame(uint8_t *data, int len);
void get_eph(const char *file);

32
libraries/SondeLib/rsc.cpp Executable file
View File

@ -0,0 +1,32 @@
/*
* dxlAPRS toolchain
*
* Copyright (C) Christian Rabler <oe5dxl@oevsv.at>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#ifndef inttypes_h
#include <inttypes.h>
#endif
#define N 255
#define R 24
#define K (N-R)
void *init_rs_char(int symsize,int gfpoly,int fcr,int prim,int nroots,int pad);
int decode_rs_char(void *arg,
unsigned char *data, int *eras_pos, int no_eras);
void *rs;
void initrsc()
{
rs = init_rs_char( 8, 0x11d, 0, 1, R, 0);
}
int decodersc(char *data, uint32_t *eras_pos, uint32_t no_eras)
{
return decode_rs_char(rs, (unsigned char *)data, (int *)eras_pos, no_eras);
}

17
libraries/SondeLib/rsc.h Executable file
View File

@ -0,0 +1,17 @@
/*
* dxlAPRS toolchain
*
* Copyright (C) Christian Rabler <oe5dxl@oevsv.at>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#ifndef rsc_H_
#define rsc_H_
long decodersc(char [], uint32_t [], uint32_t);
void initrsc(void);
#endif /* rsc_H_ */

436
libraries/SondeLib/rsc_decode.cpp Executable file
View File

@ -0,0 +1,436 @@
/*
* Copyright 2016 Hannes Schmelzer, OE5HPM
* doing several cleanups and architecture changes, no functional change yet
*
* General purpose Reed-Solomon decoder for 8-bit symbols or less
* Copyright 2003 Phil Karn, KA9Q
* May be used under the terms of the GNU Lesser General Public License (LGPL)
*
* The guts of the Reed-Solomon decoder, meant to be #included
* into a function body with the following typedefs, macros and variables supplied
* according to the code parameters:
* data_t - a typedef for the data symbol
* data_t data[] - array of rs->nn data and parity symbols to be corrected in place
* retval - an integer lvalue into which the decoder's return code is written
* NROOTS - the number of roots in the RS code generator polynomial,
* which is the same as the number of parity symbols in a block.
Integer variable or literal.
* rs->nn - the total number of symbols in a RS block. Integer variable or literal.
* rs->pad - the number of pad symbols in a block. Integer variable or literal.
* rs->alpha_to - The address of an array of rs->nn elements to convert Galois field
* elements in index (log) form to polynomial form. Read only.
* rs->index_of - The address of an array of rs->nn elements to convert Galois field
* elements in polynomial form to index (log) form. Read only.
* MODNN - a function to reduce its argument modulo rs->nn. May be inline or a macro.
* rs->fcr - An integer literal or variable specifying the first consecutive root of the
* Reed-Solomon generator polynomial. Integer variable or literal.
* rs->prim - The primitive root of the generator poly. Integer variable or literal.
* DEBUG - If set to 1 or more, do various internal consistency checking. Leave this
* undefined for production code
* The memset(), memmove(), and memcpy() functions are used. The appropriate header
* file declaring these functions (usually <string.h>) must be included by the calling
* program.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct rs {
unsigned int magic; /* struct magic */
int mm; /* Bits per symbol */
int nn; /* Symbols per block (= (1<<mm)-1) */
unsigned char *alpha_to; /* log lookup table */
unsigned char *index_of; /* Antilog lookup table */
unsigned char *genpoly; /* Generator polynomial */
int nroots; /*
* Number of generator
* roots = number of parity symbols
*/
int fcr; /* First consecutive root, index form */
int prim; /* Primitive element, index form */
int iprim; /* prim-th root of 1, index form */
int pad; /* Padding bytes in shortened block */
};
static inline int modnn(struct rs *rs,int x)
{
while (x >= rs->nn) {
x -= rs->nn;
x = (x >> rs->mm) + (x & rs->nn);
}
return x;
}
#define MODNN(x) modnn(rs, x)
#define MIN(a,b) ((a) < (b) ? (a) : (b))
#define MAGIC 0xABCD6722
void free_rs_char(void *arg)
{
struct rs *rs = (struct rs *)arg;
if (rs == NULL)
return;
if (rs->magic != MAGIC)
return;
if (rs->alpha_to != NULL)
free(rs->alpha_to);
if (rs->index_of != NULL)
free(rs->index_of);
if (rs->genpoly != NULL)
free(rs->genpoly);
free(rs);
}
/* Initialize a Reed-Solomon codec
* symsize = symbol size, bits
* gfpoly = Field generator polynomial coefficients
* fcr = first root of RS code generator polynomial, index form
* prim = primitive element to generate polynomial roots
* nroots = RS code generator polynomial degree (number of roots)
* pad = padding bytes at front of shortened block
*/
void *init_rs_char(int symsize, int gfpoly, int fcr, int prim,
int nroots, int pad)
{
struct rs *rs;
int i, j, sr,root,iprim;
/* Check parameter ranges */
if (symsize < 0 || symsize > 8*sizeof(unsigned char))
return NULL;
if (fcr < 0 || fcr >= (1<<symsize))
return NULL;
if (prim <= 0 || prim >= (1<<symsize))
return NULL;
if (nroots < 0 || nroots >= (1<<symsize))
return NULL;
if (pad < 0 || pad >= ((1<<symsize) -1 - nroots))
return NULL;
rs = (struct rs*)malloc(sizeof(*rs));
if (rs == NULL) {
printf("%s: cannot allocate memory!\n", __func__);
return NULL;
}
memset(rs, 0, sizeof(*rs));
rs->magic = MAGIC;
rs->mm = symsize;
rs->nn = (1<<symsize)-1;
rs->pad = pad;
rs->alpha_to = (unsigned char *)malloc(sizeof(unsigned char)*(rs->nn+1));
if (rs->alpha_to == NULL) {
free(rs);
return NULL;
}
rs->index_of = (unsigned char *)malloc(sizeof(unsigned char)*(rs->nn+1));
if (rs->index_of == NULL) {
free(rs->alpha_to);
free(rs);
return NULL;
}
/* Generate Galois field lookup tables */
rs->index_of[0] = rs->nn; /* log(zero) = -inf */
rs->alpha_to[rs->nn] = 0; /* alpha**-inf = 0 */
sr = 1;
for (i = 0; i < rs->nn; i++) {
rs->index_of[sr] = i;
rs->alpha_to[i] = sr;
sr <<= 1;
if (sr & (1<<symsize))
sr ^= gfpoly;
sr &= rs->nn;
}
if (sr != 1) {
/* field generator polynomial is not primitive! */
free(rs->alpha_to);
free(rs->index_of);
free(rs);
return NULL;
}
/* Form RS code generator polynomial from its roots */
rs->genpoly = (unsigned char *)malloc(sizeof(unsigned char)*(nroots+1));
if(rs->genpoly == NULL) {
free(rs->alpha_to);
free(rs->index_of);
free(rs);
return NULL;
}
rs->fcr = fcr;
rs->prim = prim;
rs->nroots = nroots;
/* Find prim-th root of 1, used in decoding */
for (iprim = 1; (iprim % prim) != 0; iprim += rs->nn)
;
rs->iprim = iprim / prim;
rs->genpoly[0] = 1;
for (i = 0, root = fcr*prim; i < nroots; i++, root += prim) {
rs->genpoly[i+1] = 1;
/* Multiply rs->genpoly[] by @**(root + x) */
for (j = i; j > 0; j--) {
if (rs->genpoly[j] != 0)
rs->genpoly[j] = rs->genpoly[j-1] ^ rs->alpha_to[modnn(rs,rs->index_of[rs->genpoly[j]] + root)];
else
rs->genpoly[j] = rs->genpoly[j-1];
}
/* rs->genpoly[0] can never be zero */
rs->genpoly[0] = rs->alpha_to[modnn(rs,rs->index_of[rs->genpoly[0]] + root)];
}
/* convert rs->genpoly[] to index form for quicker encoding */
for (i = 0; i <= nroots; i++)
rs->genpoly[i] = rs->index_of[rs->genpoly[i]];
return rs;
}
int decode_rs_char(void *arg,
unsigned char *data, int *eras_pos, int no_eras)
{
struct rs *rs = (struct rs *)arg;
if (rs == NULL)
return -1;
if (rs->magic != MAGIC)
return -1;
int retval;
int deg_lambda, el, deg_omega;
int i, j, r,k;
unsigned char u,q,tmp,num1,num2,den,discr_r;
unsigned char lambda[rs->nroots+1], s[rs->nroots]; /* Err+Eras Locator poly
* and syndrome poly */
unsigned char b[rs->nroots+1], t[rs->nroots+1], omega[rs->nroots+1];
unsigned char root[rs->nroots], reg[rs->nroots+1], loc[rs->nroots];
int syn_error, count;
/* form the syndromes; i.e., evaluate data(x) at roots of g(x) */
for (i = 0; i < rs->nroots; i++)
s[i] = data[0];
for (j = 1; j < rs->nn-rs->pad; j++) {
for(i=0;i<rs->nroots;i++) {
if(s[i] == 0) {
s[i] = data[j];
} else {
s[i] = data[j] ^ rs->alpha_to[MODNN(rs->index_of[s[i]] + (rs->fcr+i)*rs->prim)];
}
}
}
/* Convert syndromes to index form, checking for nonzero condition */
syn_error = 0;
for (i = 0; i < rs->nroots; i++) {
syn_error |= s[i];
s[i] = rs->index_of[s[i]];
}
if (!syn_error) {
/* if syndrome is zero, data[] is a codeword and there are no
* errors to correct. So return data[] unmodified
*/
count = 0;
goto finish;
}
memset(&lambda[1], 0, rs->nroots*sizeof(lambda[0]));
lambda[0] = 1;
if (no_eras > 0) {
/* Init lambda to be the erasure locator polynomial */
lambda[1] = rs->alpha_to[MODNN(rs->prim*(rs->nn-1-eras_pos[0]))];
for (i = 1; i < no_eras; i++) {
u = MODNN(rs->prim*(rs->nn-1-eras_pos[i]));
for (j = i+1; j > 0; j--) {
tmp = rs->index_of[lambda[j - 1]];
if(tmp != rs->nn)
lambda[j] ^= rs->alpha_to[MODNN(u + tmp)];
}
}
#if DEBUG >= 1
/* Test code that verifies the erasure locator polynomial just constructed
Needed only for decoder debugging. */
/* find roots of the erasure location polynomial */
for(i=1;i<=no_eras;i++)
reg[i] = rs->index_of[lambda[i]];
count = 0;
for (i = 1,k=rs->iprim-1; i <= rs->nn; i++,k = MODNN(k+rs->iprim)) {
q = 1;
for (j = 1; j <= no_eras; j++)
if (reg[j] != rs->nn) {
reg[j] = MODNN(reg[j] + j);
q ^= rs->alpha_to[reg[j]];
}
if (q != 0)
continue;
/* store root and error location number indices */
root[count] = i;
loc[count] = k;
count++;
}
if (count != no_eras) {
printf("count = %d no_eras = %d\n lambda(x) is WRONG\n",count,no_eras);
count = -1;
goto finish;
}
#if DEBUG >= 2
printf("\n Erasure positions as determined by roots of Eras Loc Poly:\n");
for (i = 0; i < count; i++)
printf("%d ", loc[i]);
printf("\n");
#endif
#endif
}
for (i = 0; i < rs->nroots+1; i++)
b[i] = rs->index_of[lambda[i]];
/*
* Begin Berlekamp-Massey algorithm to determine error+erasure
* locator polynomial
*/
r = no_eras;
el = no_eras;
while (++r <= rs->nroots) { /* r is the step number */
/* Compute discrepancy at the r-th step in poly-form */
discr_r = 0;
for (i = 0; i < r; i++) {
if ((lambda[i] != 0) && (s[r-i-1] != rs->nn)) {
discr_r ^= rs->alpha_to[MODNN(rs->index_of[lambda[i]] + s[r-i-1])];
}
}
discr_r = rs->index_of[discr_r]; /* Index form */
if (discr_r == rs->nn) {
/* 2 lines below: B(x) <-- x*B(x) */
memmove(&b[1],b,rs->nroots*sizeof(b[0]));
b[0] = rs->nn;
} else {
/* 7 lines below: T(x) <-- lambda(x) - discr_r*x*b(x) */
t[0] = lambda[0];
for (i = 0 ; i < rs->nroots; i++) {
if(b[i] != rs->nn)
t[i+1] = lambda[i+1] ^ rs->alpha_to[MODNN(discr_r + b[i])];
else
t[i+1] = lambda[i+1];
}
if (2 * el <= r + no_eras - 1) {
el = r + no_eras - el;
/*
* 2 lines below: B(x) <-- inv(discr_r) *
* lambda(x)
*/
for (i = 0; i <= rs->nroots; i++)
b[i] = (lambda[i] == 0) ? rs->nn : MODNN(rs->index_of[lambda[i]] - discr_r + rs->nn);
} else {
/* 2 lines below: B(x) <-- x*B(x) */
memmove(&b[1],b,rs->nroots*sizeof(b[0]));
b[0] = rs->nn;
}
memcpy(lambda,t,(rs->nroots+1)*sizeof(t[0]));
}
}
/* Convert lambda to index form and compute deg(lambda(x)) */
deg_lambda = 0;
for (i = 0;i < rs->nroots+1; i++){
lambda[i] = rs->index_of[lambda[i]];
if(lambda[i] != rs->nn)
deg_lambda = i;
}
/* Find roots of the error+erasure locator polynomial by Chien search */
memcpy(&reg[1], &lambda[1], rs->nroots*sizeof(reg[0]));
count = 0; /* Number of roots of lambda(x) */
for (i = 1,k=rs->iprim-1; i <= rs->nn; i++,k = MODNN(k+rs->iprim)) {
q = 1; /* lambda[0] is always 0 */
for (j = deg_lambda; j > 0; j--) {
if (reg[j] != rs->nn) {
reg[j] = MODNN(reg[j] + j);
q ^= rs->alpha_to[reg[j]];
}
}
if (q != 0)
continue; /* Not a root */
/* store root (index-form) and error location number */
#if DEBUG>=2
printf("count %d root %d loc %d\n",count,i,k);
#endif
root[count] = i;
loc[count] = k;
/* If we've already found max possible roots,
* abort the search to save time
*/
if(++count == deg_lambda)
break;
}
if (deg_lambda != count) {
/*
* deg(lambda) unequal to number of roots => uncorrectable
* error detected
*/
count = -1;
goto finish;
}
/*
* Compute err+eras evaluator poly omega(x) = s(x)*lambda(x) (modulo
* x**rs->nroots). in index form. Also find deg(omega).
*/
deg_omega = deg_lambda-1;
for (i = 0; i <= deg_omega;i++) {
tmp = 0;
for (j = i; j >= 0; j--) {
if ((s[i - j] != rs->nn) && (lambda[j] != rs->nn))
tmp ^= rs->alpha_to[MODNN(s[i - j] + lambda[j])];
}
omega[i] = rs->index_of[tmp];
}
/*
* Compute error values in poly-form. num1 = omega(inv(X(l))), num2 =
* inv(X(l))**(rs->fcr-1) and den = lambda_pr(inv(X(l))) all in poly-form
*/
for (j = count-1; j >=0; j--) {
num1 = 0;
for (i = deg_omega; i >= 0; i--) {
if (omega[i] != rs->nn)
num1 ^= rs->alpha_to[MODNN(omega[i] + i * root[j])];
}
num2 = rs->alpha_to[MODNN(root[j] * (rs->fcr - 1) + rs->nn)];
den = 0;
/* lambda[i+1] for i even is the formal derivative lambda_pr of lambda[i] */
for (i = MIN(deg_lambda, rs->nroots-1) & ~1; i >= 0; i -=2) {
if(lambda[i+1] != rs->nn)
den ^= rs->alpha_to[MODNN(lambda[i+1] + i * root[j])];
}
#if DEBUG >= 1
if (den == 0) {
printf("\n ERROR: denominator = 0\n");
count = -1;
goto finish;
}
#endif
/* Apply error to data */
if (num1 != 0 && loc[j] >= rs->pad) {
data[loc[j]-rs->pad] ^= rs->alpha_to[MODNN(rs->index_of[num1] + rs->index_of[num2] + rs->nn - rs->index_of[den])];
}
}
finish:
if(eras_pos != NULL) {
for (i = 0; i < count; i++)
eras_pos[i] = loc[i];
}
retval = count;
return retval;
}

179
libraries/SondeLib/sd.h Executable file
View File

@ -0,0 +1,179 @@
#include "FS.h"
#include "SD_MMC.h"
//Write data RadioSonde on SD
//Fonction de gestion SD
void listDir(fs::FS &fs, const char * dirname, uint8_t levels){
Serial.printf("Listing directory: %s\n", dirname);
File root = fs.open(dirname);
if(!root){
Serial.println("Failed to open directory");
return;
}
if(!root.isDirectory()){
Serial.println("Not a directory");
return;
}
File file = root.openNextFile();
while(file){
if(file.isDirectory()){
Serial.print(" DIR : ");
Serial.println(file.name());
if(levels){
listDir(fs, file.name(), levels -1);
}
} else {
Serial.print(" FILE: ");
Serial.print(file.name());
Serial.print(" SIZE: ");
Serial.println(file.size());
}
file = root.openNextFile();
}
}
void createDir(fs::FS &fs, const char * path){
Serial.printf("Creating Dir: %s\n", path);
if(fs.mkdir(path)){
Serial.println("Dir created");
} else {
Serial.println("mkdir failed");
}
}
void readFile(fs::FS &fs, const char * path){
Serial.printf("Reading file: %s\n", path);
File file = fs.open(path);
if(!file){
Serial.println("Failed to open file for reading");
return;
}
Serial.print("Read from file: ");
while(file.available()){
Serial.write(file.read());
}
}
void appendFile(fs::FS &fs, const char * path, const char * message){
Serial.printf("Appending to file: %s\n", path);
File file = fs.open(path, FILE_APPEND);
if(!file){
Serial.println("Failed to open file for appending");
return;
}
if(file.print(message)){
Serial.println("Message appended");
} else {
Serial.println("Append failed");
}
}
void writeFile(fs::FS &fs, const char * path, const char * message){
Serial.printf("Writing file: %s\n", path);
File file = fs.open(path, FILE_WRITE);
if(!file){
Serial.println("Failed to open file for writing");
return;
}
if(file.print(message)){
Serial.println("File written");
} else {
Serial.println("Write failed");
}
}
void sdwrite()
{
Serial.print("Detect SD\n");
if(SD_MMC.begin()){
uint8_t cardType = SD_MMC.cardType();
if(cardType != CARD_NONE){
Serial.print("SD_MMC Card Type: ");
if(cardType == CARD_MMC){
Serial.println("MMC");
} else if(cardType == CARD_SD){
Serial.println("SDSC");
} else if(cardType == CARD_SDHC){
Serial.println("SDHC");
} else {
Serial.println("UNKNOWN");
}
uint64_t cardSize = SD_MMC.cardSize() / (1024 * 1024);
Serial.printf("SD_MMC Card Size: %lluMB\n", cardSize);
/*
Serial.printf("Total space: %lluMB\n", SD_MMC.totalBytes() / (1024 * 1024));
Serial.printf("Used space: %lluMB\n", SD_MMC.usedBytes() / (1024 * 1024));
*/
if (sonde.si()->validPos) {
char buff[20];
char chemin[40];
//String repertoire="/radiosonde/", name=sonde.si()->ser;
//repertoire.toCharArray(chemin, 40);
//if (!SD_MMC.exists(chemin)) {
createDir(SD_MMC, "/radiosonde/");
Serial.printf("\nWrite Rep\n");
//listDir(SD_MMC, chemin, 0);
//}
//if (name.length()>8) {
//name.concat(".csv");
//repertoire.concat(name);
//repertoire.toCharArray(chemin, 40);
writeFile(SD_MMC, "/radiosonde/sonde.csv", "");
Serial.printf("\nWrite file\n");
//if (!SD_MMC.exists(chemin)) {
appendFile(SD_MMC, "/radiosonde/sonde.csv", "RadioSonde N; Ser ; Type ; Freq ; Alt ; VSpeed ; HSpeed ; Dir ; LaunchSite ; RSSI\n");
Serial.printf("\nWrite titre\n");
//}
Serial.printf("\nWrite Data\n");
snprintf(buff, 16, "%10c", sonde.si()->id);
appendFile(SD_MMC, "/radiosonde/sonde.csv", buff);
appendFile(SD_MMC, "/radiosonde/sonde.csv", ";");
snprintf(buff, 16, "%12c", sonde.si()->ser);
appendFile(SD_MMC, "/radiosonde/sonde.csv", buff);
appendFile(SD_MMC, "/radiosonde/sonde.csv", ";");
appendFile(SD_MMC, "/radiosonde/sonde.csv", sondeTypeStr[sonde.si()->type]);
appendFile(SD_MMC, "/radiosonde/sonde.csv", ";");
snprintf(buff, 16, "%3.3fMhz", sonde.si()->freq);
appendFile(SD_MMC, "/radiosonde/sonde.csv", buff);
appendFile(SD_MMC, "/radiosonde/sonde.csv", ";");
snprintf(buff, 16, sonde.si()->alt>=1000?" %5.0fm":" %3.1fm", sonde.si()->alt);
appendFile(SD_MMC, "/radiosonde/sonde.csv", buff);
appendFile(SD_MMC, "/radiosonde/sonde.csv", ";");
snprintf(buff, 16, "%2.2fm/s", sonde.si()->vs);
appendFile(SD_MMC, "/radiosonde/sonde.csv", buff);
appendFile(SD_MMC, "/radiosonde/sonde.csv", ";");
snprintf(buff, 16, "%2.2fm/s", sonde.si()->hs);
appendFile(SD_MMC, "/radiosonde/sonde.csv", buff);
appendFile(SD_MMC, "/radiosonde/sonde.csv", ";");
snprintf(buff, 16, "%3.0f°", sonde.si()->dir);
appendFile(SD_MMC, "/radiosonde/sonde.csv", buff);
appendFile(SD_MMC, "/radiosonde/sonde.csv", ";");
snprintf(buff, 16, "%18c", sonde.si()->launchsite);
appendFile(SD_MMC, "/radiosonde/sonde.csv", buff);
appendFile(SD_MMC, "/radiosonde/sonde.csv", ";");
snprintf(buff, 16, "%d", sonde.si()->rssi);
appendFile(SD_MMC, "/radiosonde/sonde.csv", buff);
appendFile(SD_MMC, "/radiosonde/sonde.csv", "\n");
}
}
}
}

227
libraries/fonts/FreeMono12pt7b.h Executable file
View File

@ -0,0 +1,227 @@
const uint8_t FreeMono12pt7bBitmaps[] PROGMEM = {
0x49, 0x24, 0x92, 0x48, 0x01, 0xF8, 0xE7, 0xE7, 0x67, 0x42, 0x42, 0x42,
0x42, 0x09, 0x02, 0x41, 0x10, 0x44, 0x11, 0x1F, 0xF1, 0x10, 0x4C, 0x12,
0x3F, 0xE1, 0x20, 0x48, 0x12, 0x04, 0x81, 0x20, 0x48, 0x04, 0x07, 0xA2,
0x19, 0x02, 0x40, 0x10, 0x03, 0x00, 0x3C, 0x00, 0x80, 0x10, 0x06, 0x01,
0xE0, 0xA7, 0xC0, 0x40, 0x10, 0x04, 0x00, 0x3C, 0x19, 0x84, 0x21, 0x08,
0x66, 0x0F, 0x00, 0x0C, 0x1C, 0x78, 0x01, 0xE0, 0xCC, 0x21, 0x08, 0x43,
0x30, 0x78, 0x3E, 0x30, 0x10, 0x08, 0x02, 0x03, 0x03, 0x47, 0x14, 0x8A,
0x43, 0x11, 0x8F, 0x60, 0xFD, 0xA4, 0x90, 0x05, 0x25, 0x24, 0x92, 0x48,
0x92, 0x24, 0x11, 0x24, 0x89, 0x24, 0x92, 0x92, 0x90, 0x00, 0x04, 0x02,
0x11, 0x07, 0xF0, 0xC0, 0x50, 0x48, 0x42, 0x00, 0x08, 0x04, 0x02, 0x01,
0x00, 0x87, 0xFC, 0x20, 0x10, 0x08, 0x04, 0x02, 0x00, 0x3B, 0x9C, 0xCE,
0x62, 0x00, 0xFF, 0xE0, 0xFF, 0x80, 0x00, 0x80, 0xC0, 0x40, 0x20, 0x20,
0x10, 0x10, 0x08, 0x08, 0x04, 0x04, 0x02, 0x02, 0x01, 0x01, 0x00, 0x80,
0x80, 0x40, 0x00, 0x1C, 0x31, 0x90, 0x58, 0x38, 0x0C, 0x06, 0x03, 0x01,
0x80, 0xC0, 0x60, 0x30, 0x34, 0x13, 0x18, 0x70, 0x30, 0xE1, 0x44, 0x81,
0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x81, 0x1F, 0xC0, 0x1E, 0x10, 0x90,
0x68, 0x10, 0x08, 0x0C, 0x04, 0x04, 0x04, 0x06, 0x06, 0x06, 0x06, 0x0E,
0x07, 0xFE, 0x3E, 0x10, 0x40, 0x08, 0x02, 0x00, 0x80, 0x40, 0xE0, 0x04,
0x00, 0x80, 0x10, 0x04, 0x01, 0x00, 0xD8, 0x63, 0xE0, 0x06, 0x0A, 0x0A,
0x12, 0x22, 0x22, 0x42, 0x42, 0x82, 0x82, 0xFF, 0x02, 0x02, 0x02, 0x0F,
0x7F, 0x20, 0x10, 0x08, 0x04, 0x02, 0xF1, 0x8C, 0x03, 0x00, 0x80, 0x40,
0x20, 0x18, 0x16, 0x18, 0xF0, 0x0F, 0x8C, 0x08, 0x08, 0x04, 0x04, 0x02,
0x79, 0x46, 0xC1, 0xE0, 0x60, 0x28, 0x14, 0x19, 0x08, 0x78, 0xFF, 0x81,
0x81, 0x02, 0x02, 0x02, 0x02, 0x04, 0x04, 0x04, 0x04, 0x08, 0x08, 0x08,
0x08, 0x3E, 0x31, 0xB0, 0x70, 0x18, 0x0C, 0x05, 0x8C, 0x38, 0x63, 0x40,
0x60, 0x30, 0x18, 0x1B, 0x18, 0xF8, 0x3C, 0x31, 0x30, 0x50, 0x28, 0x0C,
0x0F, 0x06, 0x85, 0x3C, 0x80, 0x40, 0x40, 0x20, 0x20, 0x63, 0xE0, 0xFF,
0x80, 0x07, 0xFC, 0x39, 0xCE, 0x00, 0x00, 0x06, 0x33, 0x98, 0xC4, 0x00,
0x00, 0xC0, 0x60, 0x18, 0x0C, 0x06, 0x01, 0x80, 0x0C, 0x00, 0x60, 0x03,
0x00, 0x30, 0x01, 0x00, 0xFF, 0xF0, 0x00, 0x00, 0x0F, 0xFF, 0xC0, 0x06,
0x00, 0x30, 0x01, 0x80, 0x18, 0x01, 0x80, 0xC0, 0x30, 0x18, 0x0C, 0x02,
0x00, 0x00, 0x3E, 0x60, 0xA0, 0x20, 0x10, 0x08, 0x08, 0x18, 0x10, 0x08,
0x00, 0x00, 0x00, 0x01, 0xC0, 0xE0, 0x1C, 0x31, 0x10, 0x50, 0x28, 0x14,
0x3A, 0x25, 0x22, 0x91, 0x4C, 0xA3, 0xF0, 0x08, 0x02, 0x01, 0x80, 0x7C,
0x3F, 0x00, 0x0C, 0x00, 0x48, 0x01, 0x20, 0x04, 0x40, 0x21, 0x00, 0x84,
0x04, 0x08, 0x1F, 0xE0, 0x40, 0x82, 0x01, 0x08, 0x04, 0x20, 0x13, 0xE1,
0xF0, 0xFF, 0x08, 0x11, 0x01, 0x20, 0x24, 0x04, 0x81, 0x1F, 0xC2, 0x06,
0x40, 0x68, 0x05, 0x00, 0xA0, 0x14, 0x05, 0xFF, 0x00, 0x1E, 0x48, 0x74,
0x05, 0x01, 0x80, 0x20, 0x08, 0x02, 0x00, 0x80, 0x20, 0x04, 0x01, 0x01,
0x30, 0x87, 0xC0, 0xFE, 0x10, 0x44, 0x09, 0x02, 0x40, 0x50, 0x14, 0x05,
0x01, 0x40, 0x50, 0x14, 0x0D, 0x02, 0x41, 0x3F, 0x80, 0xFF, 0xC8, 0x09,
0x01, 0x20, 0x04, 0x00, 0x88, 0x1F, 0x02, 0x20, 0x40, 0x08, 0x01, 0x00,
0xA0, 0x14, 0x03, 0xFF, 0xC0, 0xFF, 0xE8, 0x05, 0x00, 0xA0, 0x04, 0x00,
0x88, 0x1F, 0x02, 0x20, 0x40, 0x08, 0x01, 0x00, 0x20, 0x04, 0x01, 0xF0,
0x00, 0x1F, 0x46, 0x19, 0x01, 0x60, 0x28, 0x01, 0x00, 0x20, 0x04, 0x00,
0x83, 0xF0, 0x0B, 0x01, 0x20, 0x23, 0x0C, 0x3E, 0x00, 0xE1, 0xD0, 0x24,
0x09, 0x02, 0x40, 0x90, 0x27, 0xF9, 0x02, 0x40, 0x90, 0x24, 0x09, 0x02,
0x40, 0xB8, 0x70, 0xFE, 0x20, 0x40, 0x81, 0x02, 0x04, 0x08, 0x10, 0x20,
0x40, 0x81, 0x1F, 0xC0, 0x0F, 0xE0, 0x10, 0x02, 0x00, 0x40, 0x08, 0x01,
0x00, 0x20, 0x04, 0x80, 0x90, 0x12, 0x02, 0x40, 0xC6, 0x30, 0x7C, 0x00,
0xF1, 0xE4, 0x0C, 0x41, 0x04, 0x20, 0x44, 0x04, 0x80, 0x5C, 0x06, 0x60,
0x43, 0x04, 0x10, 0x40, 0x84, 0x08, 0x40, 0xCF, 0x07, 0xF8, 0x04, 0x00,
0x80, 0x10, 0x02, 0x00, 0x40, 0x08, 0x01, 0x00, 0x20, 0x04, 0x04, 0x80,
0x90, 0x12, 0x03, 0xFF, 0xC0, 0xE0, 0x3B, 0x01, 0x94, 0x14, 0xA0, 0xA4,
0x89, 0x24, 0x49, 0x14, 0x48, 0xA2, 0x45, 0x12, 0x10, 0x90, 0x04, 0x80,
0x24, 0x01, 0x78, 0x3C, 0xE0, 0xF6, 0x02, 0x50, 0x25, 0x02, 0x48, 0x24,
0xC2, 0x44, 0x24, 0x22, 0x43, 0x24, 0x12, 0x40, 0xA4, 0x0A, 0x40, 0x6F,
0x06, 0x0F, 0x03, 0x0C, 0x60, 0x64, 0x02, 0x80, 0x18, 0x01, 0x80, 0x18,
0x01, 0x80, 0x18, 0x01, 0x40, 0x26, 0x06, 0x30, 0xC0, 0xF0, 0xFF, 0x10,
0x64, 0x05, 0x01, 0x40, 0x50, 0x34, 0x19, 0xFC, 0x40, 0x10, 0x04, 0x01,
0x00, 0x40, 0x3E, 0x00, 0x0F, 0x03, 0x0C, 0x60, 0x64, 0x02, 0x80, 0x18,
0x01, 0x80, 0x18, 0x01, 0x80, 0x18, 0x01, 0x40, 0x26, 0x06, 0x30, 0xC1,
0xF0, 0x0C, 0x01, 0xF1, 0x30, 0xE0, 0xFF, 0x04, 0x18, 0x40, 0xC4, 0x04,
0x40, 0x44, 0x0C, 0x41, 0x87, 0xE0, 0x43, 0x04, 0x10, 0x40, 0x84, 0x04,
0x40, 0x4F, 0x03, 0x1F, 0x48, 0x34, 0x05, 0x01, 0x40, 0x08, 0x01, 0xC0,
0x0E, 0x00, 0x40, 0x18, 0x06, 0x01, 0xE1, 0xA7, 0xC0, 0xFF, 0xF0, 0x86,
0x10, 0x82, 0x00, 0x40, 0x08, 0x01, 0x00, 0x20, 0x04, 0x00, 0x80, 0x10,
0x02, 0x00, 0x40, 0x7F, 0x00, 0xF0, 0xF4, 0x02, 0x40, 0x24, 0x02, 0x40,
0x24, 0x02, 0x40, 0x24, 0x02, 0x40, 0x24, 0x02, 0x40, 0x22, 0x04, 0x30,
0xC0, 0xF0, 0xF8, 0x7C, 0x80, 0x22, 0x01, 0x04, 0x04, 0x10, 0x20, 0x40,
0x80, 0x82, 0x02, 0x10, 0x08, 0x40, 0x11, 0x00, 0x48, 0x01, 0xA0, 0x03,
0x00, 0x0C, 0x00, 0xF8, 0x7C, 0x80, 0x22, 0x00, 0x88, 0xC2, 0x23, 0x10,
0x8E, 0x42, 0x29, 0x09, 0x24, 0x24, 0x90, 0x91, 0x41, 0x85, 0x06, 0x14,
0x18, 0x70, 0x60, 0x80, 0xF0, 0xF2, 0x06, 0x30, 0x41, 0x08, 0x09, 0x80,
0x50, 0x06, 0x00, 0x60, 0x0D, 0x00, 0x88, 0x10, 0xC2, 0x04, 0x60, 0x2F,
0x0F, 0xF0, 0xF2, 0x02, 0x10, 0x41, 0x04, 0x08, 0x80, 0x50, 0x05, 0x00,
0x20, 0x02, 0x00, 0x20, 0x02, 0x00, 0x20, 0x02, 0x01, 0xFC, 0xFF, 0x40,
0xA0, 0x90, 0x40, 0x40, 0x40, 0x20, 0x20, 0x20, 0x10, 0x50, 0x30, 0x18,
0x0F, 0xFC, 0xF2, 0x49, 0x24, 0x92, 0x49, 0x24, 0x9C, 0x80, 0x60, 0x10,
0x08, 0x02, 0x01, 0x00, 0x40, 0x20, 0x08, 0x04, 0x01, 0x00, 0x80, 0x20,
0x10, 0x04, 0x02, 0x00, 0x80, 0x40, 0xE4, 0x92, 0x49, 0x24, 0x92, 0x49,
0x3C, 0x08, 0x0C, 0x09, 0x0C, 0x4C, 0x14, 0x04, 0xFF, 0xFC, 0x84, 0x21,
0x3E, 0x00, 0x60, 0x08, 0x02, 0x3F, 0x98, 0x28, 0x0A, 0x02, 0xC3, 0x9F,
0x30, 0xE0, 0x01, 0x00, 0x08, 0x00, 0x40, 0x02, 0x00, 0x13, 0xE0, 0xA0,
0x86, 0x02, 0x20, 0x09, 0x00, 0x48, 0x02, 0x40, 0x13, 0x01, 0x14, 0x1B,
0x9F, 0x00, 0x1F, 0x4C, 0x19, 0x01, 0x40, 0x28, 0x01, 0x00, 0x20, 0x02,
0x00, 0x60, 0x43, 0xF0, 0x00, 0xC0, 0x08, 0x01, 0x00, 0x20, 0x04, 0x3C,
0x98, 0x52, 0x06, 0x80, 0x50, 0x0A, 0x01, 0x40, 0x24, 0x0C, 0xC2, 0x87,
0x98, 0x3F, 0x18, 0x68, 0x06, 0x01, 0xFF, 0xE0, 0x08, 0x03, 0x00, 0x60,
0xC7, 0xC0, 0x0F, 0x98, 0x08, 0x04, 0x02, 0x07, 0xF8, 0x80, 0x40, 0x20,
0x10, 0x08, 0x04, 0x02, 0x01, 0x03, 0xF8, 0x1E, 0x6C, 0x39, 0x03, 0x40,
0x28, 0x05, 0x00, 0xA0, 0x12, 0x06, 0x61, 0x43, 0xC8, 0x01, 0x00, 0x20,
0x08, 0x3E, 0x00, 0xC0, 0x10, 0x04, 0x01, 0x00, 0x40, 0x13, 0x87, 0x11,
0x82, 0x40, 0x90, 0x24, 0x09, 0x02, 0x40, 0x90, 0x2E, 0x1C, 0x08, 0x04,
0x02, 0x00, 0x00, 0x03, 0xC0, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01, 0x00,
0x80, 0x43, 0xFE, 0x04, 0x08, 0x10, 0x00, 0x1F, 0xC0, 0x81, 0x02, 0x04,
0x08, 0x10, 0x20, 0x40, 0x81, 0x02, 0x0B, 0xE0, 0xE0, 0x02, 0x00, 0x20,
0x02, 0x00, 0x20, 0x02, 0x3C, 0x21, 0x02, 0x60, 0x2C, 0x03, 0x80, 0x24,
0x02, 0x20, 0x21, 0x02, 0x08, 0xE1, 0xF0, 0x78, 0x04, 0x02, 0x01, 0x00,
0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01, 0x00, 0x80, 0x43, 0xFE,
0xDC, 0xE3, 0x19, 0x90, 0x84, 0x84, 0x24, 0x21, 0x21, 0x09, 0x08, 0x48,
0x42, 0x42, 0x17, 0x18, 0xC0, 0x67, 0x83, 0x84, 0x20, 0x22, 0x02, 0x20,
0x22, 0x02, 0x20, 0x22, 0x02, 0x20, 0x2F, 0x07, 0x1F, 0x04, 0x11, 0x01,
0x40, 0x18, 0x03, 0x00, 0x60, 0x0A, 0x02, 0x20, 0x83, 0xE0, 0xCF, 0x85,
0x06, 0x60, 0x24, 0x01, 0x40, 0x14, 0x01, 0x40, 0x16, 0x02, 0x50, 0x44,
0xF8, 0x40, 0x04, 0x00, 0x40, 0x0F, 0x00, 0x1E, 0x6C, 0x3B, 0x03, 0x40,
0x28, 0x05, 0x00, 0xA0, 0x12, 0x06, 0x61, 0x43, 0xC8, 0x01, 0x00, 0x20,
0x04, 0x03, 0xC0, 0xE3, 0x8B, 0x13, 0x80, 0x80, 0x20, 0x08, 0x02, 0x00,
0x80, 0x20, 0x3F, 0x80, 0x1F, 0x58, 0x34, 0x05, 0x80, 0x1E, 0x00, 0x60,
0x06, 0x01, 0xC0, 0xAF, 0xC0, 0x20, 0x04, 0x00, 0x80, 0x10, 0x0F, 0xF0,
0x40, 0x08, 0x01, 0x00, 0x20, 0x04, 0x00, 0x80, 0x10, 0x03, 0x04, 0x3F,
0x00, 0xC1, 0xC8, 0x09, 0x01, 0x20, 0x24, 0x04, 0x80, 0x90, 0x12, 0x02,
0x61, 0xC7, 0xCC, 0xF8, 0xF9, 0x01, 0x08, 0x10, 0x60, 0x81, 0x08, 0x08,
0x40, 0x22, 0x01, 0x20, 0x05, 0x00, 0x30, 0x00, 0xF0, 0x7A, 0x01, 0x10,
0x08, 0x8C, 0x42, 0x62, 0x12, 0x90, 0xA5, 0x05, 0x18, 0x28, 0xC0, 0x86,
0x00, 0x78, 0xF3, 0x04, 0x18, 0x80, 0xD0, 0x06, 0x00, 0x70, 0x09, 0x81,
0x0C, 0x20, 0x6F, 0x8F, 0xF0, 0xF2, 0x02, 0x20, 0x41, 0x04, 0x10, 0x80,
0x88, 0x09, 0x00, 0x50, 0x06, 0x00, 0x20, 0x04, 0x00, 0x40, 0x08, 0x0F,
0xE0, 0xFF, 0x41, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x40, 0xBF,
0xC0, 0x19, 0x08, 0x42, 0x10, 0x84, 0x64, 0x18, 0x42, 0x10, 0x84, 0x20,
0xC0, 0xFF, 0xFF, 0xC0, 0xC1, 0x08, 0x42, 0x10, 0x84, 0x10, 0x4C, 0x42,
0x10, 0x84, 0x26, 0x00, 0x38, 0x13, 0x38, 0x38 };
const GFXglyph FreeMono12pt7bGlyphs[] PROGMEM = {
{ 0, 0, 0, 14, 0, 1 }, // 0x20 ' '
{ 0, 3, 15, 14, 6, -14 }, // 0x21 '!'
{ 6, 8, 7, 14, 3, -14 }, // 0x22 '"'
{ 13, 10, 16, 14, 2, -14 }, // 0x23 '#'
{ 33, 10, 17, 14, 2, -14 }, // 0x24 '$'
{ 55, 10, 15, 14, 2, -14 }, // 0x25 '%'
{ 74, 9, 12, 14, 3, -11 }, // 0x26 '&'
{ 88, 3, 7, 14, 5, -14 }, // 0x27 '''
{ 91, 3, 18, 14, 7, -14 }, // 0x28 '('
{ 98, 3, 18, 14, 4, -14 }, // 0x29 ')'
{ 105, 9, 9, 14, 3, -14 }, // 0x2A '*'
{ 116, 9, 11, 14, 3, -11 }, // 0x2B '+'
{ 129, 5, 7, 14, 3, -3 }, // 0x2C ','
{ 134, 11, 1, 14, 2, -6 }, // 0x2D '-'
{ 136, 3, 3, 14, 5, -2 }, // 0x2E '.'
{ 138, 9, 18, 14, 3, -15 }, // 0x2F '/'
{ 159, 9, 15, 14, 3, -14 }, // 0x30 '0'
{ 176, 7, 14, 14, 4, -13 }, // 0x31 '1'
{ 189, 9, 15, 14, 2, -14 }, // 0x32 '2'
{ 206, 10, 15, 14, 2, -14 }, // 0x33 '3'
{ 225, 8, 15, 14, 3, -14 }, // 0x34 '4'
{ 240, 9, 15, 14, 3, -14 }, // 0x35 '5'
{ 257, 9, 15, 14, 3, -14 }, // 0x36 '6'
{ 274, 8, 15, 14, 3, -14 }, // 0x37 '7'
{ 289, 9, 15, 14, 3, -14 }, // 0x38 '8'
{ 306, 9, 15, 14, 3, -14 }, // 0x39 '9'
{ 323, 3, 10, 14, 5, -9 }, // 0x3A ':'
{ 327, 5, 13, 14, 3, -9 }, // 0x3B ';'
{ 336, 11, 11, 14, 2, -11 }, // 0x3C '<'
{ 352, 12, 4, 14, 1, -8 }, // 0x3D '='
{ 358, 11, 11, 14, 2, -11 }, // 0x3E '>'
{ 374, 9, 14, 14, 3, -13 }, // 0x3F '?'
{ 390, 9, 16, 14, 3, -14 }, // 0x40 '@'
{ 408, 14, 14, 14, 0, -13 }, // 0x41 'A'
{ 433, 11, 14, 14, 2, -13 }, // 0x42 'B'
{ 453, 10, 14, 14, 2, -13 }, // 0x43 'C'
{ 471, 10, 14, 14, 2, -13 }, // 0x44 'D'
{ 489, 11, 14, 14, 2, -13 }, // 0x45 'E'
{ 509, 11, 14, 14, 2, -13 }, // 0x46 'F'
{ 529, 11, 14, 14, 2, -13 }, // 0x47 'G'
{ 549, 10, 14, 14, 2, -13 }, // 0x48 'H'
{ 567, 7, 14, 14, 4, -13 }, // 0x49 'I'
{ 580, 11, 14, 14, 2, -13 }, // 0x4A 'J'
{ 600, 12, 14, 14, 2, -13 }, // 0x4B 'K'
{ 621, 11, 14, 14, 2, -13 }, // 0x4C 'L'
{ 641, 13, 14, 14, 1, -13 }, // 0x4D 'M'
{ 664, 12, 14, 14, 1, -13 }, // 0x4E 'N'
{ 685, 12, 14, 14, 1, -13 }, // 0x4F 'O'
{ 706, 10, 14, 14, 2, -13 }, // 0x50 'P'
{ 724, 12, 17, 14, 1, -13 }, // 0x51 'Q'
{ 750, 12, 14, 14, 2, -13 }, // 0x52 'R'
{ 771, 10, 14, 14, 2, -13 }, // 0x53 'S'
{ 789, 11, 14, 14, 2, -13 }, // 0x54 'T'
{ 809, 12, 14, 14, 1, -13 }, // 0x55 'U'
{ 830, 14, 14, 14, 0, -13 }, // 0x56 'V'
{ 855, 14, 14, 14, 0, -13 }, // 0x57 'W'
{ 880, 12, 14, 14, 1, -13 }, // 0x58 'X'
{ 901, 12, 14, 14, 1, -13 }, // 0x59 'Y'
{ 922, 9, 14, 14, 3, -13 }, // 0x5A 'Z'
{ 938, 3, 18, 14, 7, -14 }, // 0x5B '['
{ 945, 9, 18, 14, 3, -15 }, // 0x5C '\'
{ 966, 3, 18, 14, 5, -14 }, // 0x5D ']'
{ 973, 9, 6, 14, 3, -14 }, // 0x5E '^'
{ 980, 14, 1, 14, 0, 3 }, // 0x5F '_'
{ 982, 4, 4, 14, 4, -15 }, // 0x60 '`'
{ 984, 10, 10, 14, 2, -9 }, // 0x61 'a'
{ 997, 13, 15, 14, 0, -14 }, // 0x62 'b'
{ 1022, 11, 10, 14, 2, -9 }, // 0x63 'c'
{ 1036, 11, 15, 14, 2, -14 }, // 0x64 'd'
{ 1057, 10, 10, 14, 2, -9 }, // 0x65 'e'
{ 1070, 9, 15, 14, 4, -14 }, // 0x66 'f'
{ 1087, 11, 14, 14, 2, -9 }, // 0x67 'g'
{ 1107, 10, 15, 14, 2, -14 }, // 0x68 'h'
{ 1126, 9, 15, 14, 3, -14 }, // 0x69 'i'
{ 1143, 7, 19, 14, 3, -14 }, // 0x6A 'j'
{ 1160, 12, 15, 14, 1, -14 }, // 0x6B 'k'
{ 1183, 9, 15, 14, 3, -14 }, // 0x6C 'l'
{ 1200, 13, 10, 14, 1, -9 }, // 0x6D 'm'
{ 1217, 12, 10, 14, 1, -9 }, // 0x6E 'n'
{ 1232, 11, 10, 14, 2, -9 }, // 0x6F 'o'
{ 1246, 12, 14, 14, 1, -9 }, // 0x70 'p'
{ 1267, 11, 14, 14, 2, -9 }, // 0x71 'q'
{ 1287, 10, 10, 14, 3, -9 }, // 0x72 'r'
{ 1300, 10, 10, 14, 2, -9 }, // 0x73 's'
{ 1313, 11, 14, 14, 1, -13 }, // 0x74 't'
{ 1333, 11, 10, 14, 2, -9 }, // 0x75 'u'
{ 1347, 13, 10, 14, 1, -9 }, // 0x76 'v'
{ 1364, 13, 10, 14, 1, -9 }, // 0x77 'w'
{ 1381, 12, 10, 14, 1, -9 }, // 0x78 'x'
{ 1396, 12, 14, 14, 1, -9 }, // 0x79 'y'
{ 1417, 9, 10, 14, 3, -9 }, // 0x7A 'z'
{ 1429, 5, 18, 14, 5, -14 }, // 0x7B '{'
{ 1441, 1, 18, 14, 7, -14 }, // 0x7C '|'
{ 1444, 5, 18, 14, 5, -14 }, // 0x7D '}'
{ 1456, 10, 3, 14, 2, -7 } }; // 0x7E '~'
const GFXfont FreeMono12pt7b PROGMEM = {
(uint8_t *)FreeMono12pt7bBitmaps,
(GFXglyph *)FreeMono12pt7bGlyphs,
0x20, 0x7E, 24 };
// Approx. 2132 bytes

176
libraries/fonts/FreeMono9pt7b.h Executable file
View File

@ -0,0 +1,176 @@
const uint8_t FreeMono9pt7bBitmaps[] PROGMEM = {
0xAA, 0xA8, 0x0C, 0xED, 0x24, 0x92, 0x48, 0x24, 0x48, 0x91, 0x2F, 0xE4,
0x89, 0x7F, 0x28, 0x51, 0x22, 0x40, 0x08, 0x3E, 0x62, 0x40, 0x30, 0x0E,
0x01, 0x81, 0xC3, 0xBE, 0x08, 0x08, 0x71, 0x12, 0x23, 0x80, 0x23, 0xB8,
0x0E, 0x22, 0x44, 0x70, 0x38, 0x81, 0x02, 0x06, 0x1A, 0x65, 0x46, 0xC8,
0xEC, 0xE9, 0x24, 0x5A, 0xAA, 0xA9, 0x40, 0xA9, 0x55, 0x5A, 0x80, 0x10,
0x22, 0x4B, 0xE3, 0x05, 0x11, 0x00, 0x10, 0x20, 0x47, 0xF1, 0x02, 0x04,
0x00, 0x6B, 0x48, 0xFF, 0x00, 0xF0, 0x02, 0x08, 0x10, 0x60, 0x81, 0x04,
0x08, 0x20, 0x41, 0x02, 0x08, 0x00, 0x38, 0x8A, 0x0C, 0x18, 0x30, 0x60,
0xC1, 0x82, 0x88, 0xE0, 0x27, 0x28, 0x42, 0x10, 0x84, 0x21, 0x3E, 0x38,
0x8A, 0x08, 0x10, 0x20, 0x82, 0x08, 0x61, 0x03, 0xF8, 0x7C, 0x06, 0x02,
0x02, 0x1C, 0x06, 0x01, 0x01, 0x01, 0x42, 0x3C, 0x18, 0xA2, 0x92, 0x8A,
0x28, 0xBF, 0x08, 0x21, 0xC0, 0x7C, 0x81, 0x03, 0xE4, 0x40, 0x40, 0x81,
0x03, 0x88, 0xE0, 0x1E, 0x41, 0x04, 0x0B, 0x98, 0xB0, 0xC1, 0xC2, 0x88,
0xE0, 0xFE, 0x04, 0x08, 0x20, 0x40, 0x82, 0x04, 0x08, 0x20, 0x40, 0x38,
0x8A, 0x0C, 0x14, 0x47, 0x11, 0x41, 0x83, 0x8C, 0xE0, 0x38, 0x8A, 0x1C,
0x18, 0x68, 0xCE, 0x81, 0x04, 0x13, 0xC0, 0xF0, 0x0F, 0x6C, 0x00, 0xD2,
0xD2, 0x00, 0x03, 0x04, 0x18, 0x60, 0x60, 0x18, 0x04, 0x03, 0xFF, 0x80,
0x00, 0x1F, 0xF0, 0x40, 0x18, 0x03, 0x00, 0x60, 0x20, 0x60, 0xC0, 0x80,
0x3D, 0x84, 0x08, 0x30, 0xC2, 0x00, 0x00, 0x00, 0x30, 0x3C, 0x46, 0x82,
0x8E, 0xB2, 0xA2, 0xA2, 0x9F, 0x80, 0x80, 0x40, 0x3C, 0x3C, 0x01, 0x40,
0x28, 0x09, 0x01, 0x10, 0x42, 0x0F, 0xC1, 0x04, 0x40, 0x9E, 0x3C, 0xFE,
0x21, 0x90, 0x48, 0x67, 0xE2, 0x09, 0x02, 0x81, 0x41, 0xFF, 0x80, 0x3E,
0xB0, 0xF0, 0x30, 0x08, 0x04, 0x02, 0x00, 0x80, 0x60, 0x8F, 0x80, 0xFE,
0x21, 0x90, 0x68, 0x14, 0x0A, 0x05, 0x02, 0x83, 0x43, 0x7F, 0x00, 0xFF,
0x20, 0x90, 0x08, 0x87, 0xC2, 0x21, 0x00, 0x81, 0x40, 0xFF, 0xC0, 0xFF,
0xA0, 0x50, 0x08, 0x87, 0xC2, 0x21, 0x00, 0x80, 0x40, 0x78, 0x00, 0x1E,
0x98, 0x6C, 0x0A, 0x00, 0x80, 0x20, 0xF8, 0x0B, 0x02, 0x60, 0x87, 0xC0,
0xE3, 0xA0, 0x90, 0x48, 0x27, 0xF2, 0x09, 0x04, 0x82, 0x41, 0x71, 0xC0,
0xF9, 0x08, 0x42, 0x10, 0x84, 0x27, 0xC0, 0x1F, 0x02, 0x02, 0x02, 0x02,
0x02, 0x82, 0x82, 0xC6, 0x78, 0xE3, 0xA1, 0x11, 0x09, 0x05, 0x83, 0x21,
0x08, 0x84, 0x41, 0x70, 0xC0, 0xE0, 0x40, 0x40, 0x40, 0x40, 0x40, 0x41,
0x41, 0x41, 0xFF, 0xE0, 0xEC, 0x19, 0x45, 0x28, 0xA4, 0xA4, 0x94, 0x91,
0x12, 0x02, 0x40, 0x5C, 0x1C, 0xC3, 0xB0, 0x94, 0x4A, 0x24, 0x92, 0x49,
0x14, 0x8A, 0x43, 0x70, 0x80, 0x1E, 0x31, 0x90, 0x50, 0x18, 0x0C, 0x06,
0x02, 0x82, 0x63, 0x0F, 0x00, 0xFE, 0x43, 0x41, 0x41, 0x42, 0x7C, 0x40,
0x40, 0x40, 0xF0, 0x1C, 0x31, 0x90, 0x50, 0x18, 0x0C, 0x06, 0x02, 0x82,
0x63, 0x1F, 0x04, 0x07, 0x92, 0x30, 0xFE, 0x21, 0x90, 0x48, 0x24, 0x23,
0xE1, 0x10, 0x84, 0x41, 0x70, 0xC0, 0x3A, 0xCD, 0x0A, 0x03, 0x01, 0x80,
0xC1, 0xC7, 0x78, 0xFF, 0xC4, 0x62, 0x21, 0x00, 0x80, 0x40, 0x20, 0x10,
0x08, 0x1F, 0x00, 0xE3, 0xA0, 0x90, 0x48, 0x24, 0x12, 0x09, 0x04, 0x82,
0x22, 0x0E, 0x00, 0xF1, 0xE8, 0x10, 0x82, 0x10, 0x42, 0x10, 0x22, 0x04,
0x80, 0x50, 0x0C, 0x00, 0x80, 0xF1, 0xE8, 0x09, 0x11, 0x25, 0x44, 0xA8,
0x55, 0x0C, 0xA1, 0x8C, 0x31, 0x84, 0x30, 0xE3, 0xA0, 0x88, 0x82, 0x80,
0x80, 0xC0, 0x90, 0x44, 0x41, 0x71, 0xC0, 0xE3, 0xA0, 0x88, 0x82, 0x81,
0x40, 0x40, 0x20, 0x10, 0x08, 0x1F, 0x00, 0xFD, 0x0A, 0x20, 0x81, 0x04,
0x10, 0x21, 0x83, 0xFC, 0xEA, 0xAA, 0xAA, 0xC0, 0x80, 0x81, 0x03, 0x02,
0x04, 0x04, 0x08, 0x08, 0x10, 0x10, 0x20, 0x20, 0xD5, 0x55, 0x55, 0xC0,
0x10, 0x51, 0x22, 0x28, 0x20, 0xFF, 0xE0, 0x88, 0x80, 0x7E, 0x00, 0x80,
0x47, 0xEC, 0x14, 0x0A, 0x0C, 0xFB, 0xC0, 0x20, 0x10, 0x0B, 0xC6, 0x12,
0x05, 0x02, 0x81, 0x40, 0xB0, 0xB7, 0x80, 0x3A, 0x8E, 0x0C, 0x08, 0x10,
0x10, 0x9E, 0x03, 0x00, 0x80, 0x47, 0xA4, 0x34, 0x0A, 0x05, 0x02, 0x81,
0x21, 0x8F, 0x60, 0x3C, 0x43, 0x81, 0xFF, 0x80, 0x80, 0x61, 0x3E, 0x3D,
0x04, 0x3E, 0x41, 0x04, 0x10, 0x41, 0x0F, 0x80, 0x3D, 0xA1, 0xA0, 0x50,
0x28, 0x14, 0x09, 0x0C, 0x7A, 0x01, 0x01, 0x87, 0x80, 0xC0, 0x20, 0x10,
0x0B, 0xC6, 0x32, 0x09, 0x04, 0x82, 0x41, 0x20, 0xB8, 0xE0, 0x10, 0x01,
0xC0, 0x81, 0x02, 0x04, 0x08, 0x11, 0xFC, 0x10, 0x3E, 0x10, 0x84, 0x21,
0x08, 0x42, 0x3F, 0x00, 0xC0, 0x40, 0x40, 0x4F, 0x44, 0x58, 0x70, 0x48,
0x44, 0x42, 0xC7, 0x70, 0x20, 0x40, 0x81, 0x02, 0x04, 0x08, 0x10, 0x23,
0xF8, 0xB7, 0x64, 0x62, 0x31, 0x18, 0x8C, 0x46, 0x23, 0x91, 0x5E, 0x31,
0x90, 0x48, 0x24, 0x12, 0x09, 0x05, 0xC7, 0x3E, 0x31, 0xA0, 0x30, 0x18,
0x0C, 0x05, 0x8C, 0x7C, 0xDE, 0x30, 0x90, 0x28, 0x14, 0x0A, 0x05, 0x84,
0xBC, 0x40, 0x20, 0x38, 0x00, 0x3D, 0xA1, 0xA0, 0x50, 0x28, 0x14, 0x09,
0x0C, 0x7A, 0x01, 0x00, 0x80, 0xE0, 0xCE, 0xA1, 0x82, 0x04, 0x08, 0x10,
0x7C, 0x3A, 0x8D, 0x0B, 0x80, 0xF0, 0x70, 0xDE, 0x40, 0x40, 0xFC, 0x40,
0x40, 0x40, 0x40, 0x40, 0x41, 0x3E, 0xC3, 0x41, 0x41, 0x41, 0x41, 0x41,
0x43, 0x3D, 0xE3, 0xA0, 0x90, 0x84, 0x42, 0x20, 0xA0, 0x50, 0x10, 0xE3,
0xC0, 0x92, 0x4B, 0x25, 0x92, 0xA9, 0x98, 0x44, 0xE3, 0x31, 0x05, 0x01,
0x01, 0x41, 0x11, 0x05, 0xC7, 0xE3, 0xA0, 0x90, 0x84, 0x42, 0x40, 0xA0,
0x60, 0x10, 0x10, 0x08, 0x3E, 0x00, 0xFD, 0x08, 0x20, 0x82, 0x08, 0x10,
0xBF, 0x29, 0x24, 0xA2, 0x49, 0x26, 0xFF, 0xF8, 0x89, 0x24, 0x8A, 0x49,
0x2C, 0x61, 0x24, 0x30 };
const GFXglyph FreeMono9pt7bGlyphs[] PROGMEM = {
{ 0, 0, 0, 11, 0, 1 }, // 0x20 ' '
{ 0, 2, 11, 11, 4, -10 }, // 0x21 '!'
{ 3, 6, 5, 11, 2, -10 }, // 0x22 '"'
{ 7, 7, 12, 11, 2, -10 }, // 0x23 '#'
{ 18, 8, 12, 11, 1, -10 }, // 0x24 '$'
{ 30, 7, 11, 11, 2, -10 }, // 0x25 '%'
{ 40, 7, 10, 11, 2, -9 }, // 0x26 '&'
{ 49, 3, 5, 11, 4, -10 }, // 0x27 '''
{ 51, 2, 13, 11, 5, -10 }, // 0x28 '('
{ 55, 2, 13, 11, 4, -10 }, // 0x29 ')'
{ 59, 7, 7, 11, 2, -10 }, // 0x2A '*'
{ 66, 7, 7, 11, 2, -8 }, // 0x2B '+'
{ 73, 3, 5, 11, 2, -1 }, // 0x2C ','
{ 75, 9, 1, 11, 1, -5 }, // 0x2D '-'
{ 77, 2, 2, 11, 4, -1 }, // 0x2E '.'
{ 78, 7, 13, 11, 2, -11 }, // 0x2F '/'
{ 90, 7, 11, 11, 2, -10 }, // 0x30 '0'
{ 100, 5, 11, 11, 3, -10 }, // 0x31 '1'
{ 107, 7, 11, 11, 2, -10 }, // 0x32 '2'
{ 117, 8, 11, 11, 1, -10 }, // 0x33 '3'
{ 128, 6, 11, 11, 3, -10 }, // 0x34 '4'
{ 137, 7, 11, 11, 2, -10 }, // 0x35 '5'
{ 147, 7, 11, 11, 2, -10 }, // 0x36 '6'
{ 157, 7, 11, 11, 2, -10 }, // 0x37 '7'
{ 167, 7, 11, 11, 2, -10 }, // 0x38 '8'
{ 177, 7, 11, 11, 2, -10 }, // 0x39 '9'
{ 187, 2, 8, 11, 4, -7 }, // 0x3A ':'
{ 189, 3, 11, 11, 3, -7 }, // 0x3B ';'
{ 194, 8, 8, 11, 1, -8 }, // 0x3C '<'
{ 202, 9, 4, 11, 1, -6 }, // 0x3D '='
{ 207, 9, 8, 11, 1, -8 }, // 0x3E '>'
{ 216, 7, 10, 11, 2, -9 }, // 0x3F '?'
{ 225, 8, 12, 11, 2, -10 }, // 0x40 '@'
{ 237, 11, 10, 11, 0, -9 }, // 0x41 'A'
{ 251, 9, 10, 11, 1, -9 }, // 0x42 'B'
{ 263, 9, 10, 11, 1, -9 }, // 0x43 'C'
{ 275, 9, 10, 11, 1, -9 }, // 0x44 'D'
{ 287, 9, 10, 11, 1, -9 }, // 0x45 'E'
{ 299, 9, 10, 11, 1, -9 }, // 0x46 'F'
{ 311, 10, 10, 11, 1, -9 }, // 0x47 'G'
{ 324, 9, 10, 11, 1, -9 }, // 0x48 'H'
{ 336, 5, 10, 11, 3, -9 }, // 0x49 'I'
{ 343, 8, 10, 11, 2, -9 }, // 0x4A 'J'
{ 353, 9, 10, 11, 1, -9 }, // 0x4B 'K'
{ 365, 8, 10, 11, 2, -9 }, // 0x4C 'L'
{ 375, 11, 10, 11, 0, -9 }, // 0x4D 'M'
{ 389, 9, 10, 11, 1, -9 }, // 0x4E 'N'
{ 401, 9, 10, 11, 1, -9 }, // 0x4F 'O'
{ 413, 8, 10, 11, 1, -9 }, // 0x50 'P'
{ 423, 9, 13, 11, 1, -9 }, // 0x51 'Q'
{ 438, 9, 10, 11, 1, -9 }, // 0x52 'R'
{ 450, 7, 10, 11, 2, -9 }, // 0x53 'S'
{ 459, 9, 10, 11, 1, -9 }, // 0x54 'T'
{ 471, 9, 10, 11, 1, -9 }, // 0x55 'U'
{ 483, 11, 10, 11, 0, -9 }, // 0x56 'V'
{ 497, 11, 10, 11, 0, -9 }, // 0x57 'W'
{ 511, 9, 10, 11, 1, -9 }, // 0x58 'X'
{ 523, 9, 10, 11, 1, -9 }, // 0x59 'Y'
{ 535, 7, 10, 11, 2, -9 }, // 0x5A 'Z'
{ 544, 2, 13, 11, 5, -10 }, // 0x5B '['
{ 548, 7, 13, 11, 2, -11 }, // 0x5C '\'
{ 560, 2, 13, 11, 4, -10 }, // 0x5D ']'
{ 564, 7, 5, 11, 2, -10 }, // 0x5E '^'
{ 569, 11, 1, 11, 0, 2 }, // 0x5F '_'
{ 571, 3, 3, 11, 3, -11 }, // 0x60 '`'
{ 573, 9, 8, 11, 1, -7 }, // 0x61 'a'
{ 582, 9, 11, 11, 1, -10 }, // 0x62 'b'
{ 595, 7, 8, 11, 2, -7 }, // 0x63 'c'
{ 602, 9, 11, 11, 1, -10 }, // 0x64 'd'
{ 615, 8, 8, 11, 1, -7 }, // 0x65 'e'
{ 623, 6, 11, 11, 3, -10 }, // 0x66 'f'
{ 632, 9, 11, 11, 1, -7 }, // 0x67 'g'
{ 645, 9, 11, 11, 1, -10 }, // 0x68 'h'
{ 658, 7, 10, 11, 2, -9 }, // 0x69 'i'
{ 667, 5, 13, 11, 3, -9 }, // 0x6A 'j'
{ 676, 8, 11, 11, 2, -10 }, // 0x6B 'k'
{ 687, 7, 11, 11, 2, -10 }, // 0x6C 'l'
{ 697, 9, 8, 11, 1, -7 }, // 0x6D 'm'
{ 706, 9, 8, 11, 1, -7 }, // 0x6E 'n'
{ 715, 9, 8, 11, 1, -7 }, // 0x6F 'o'
{ 724, 9, 11, 11, 1, -7 }, // 0x70 'p'
{ 737, 9, 11, 11, 1, -7 }, // 0x71 'q'
{ 750, 7, 8, 11, 3, -7 }, // 0x72 'r'
{ 757, 7, 8, 11, 2, -7 }, // 0x73 's'
{ 764, 8, 10, 11, 2, -9 }, // 0x74 't'
{ 774, 8, 8, 11, 1, -7 }, // 0x75 'u'
{ 782, 9, 8, 11, 1, -7 }, // 0x76 'v'
{ 791, 9, 8, 11, 1, -7 }, // 0x77 'w'
{ 800, 9, 8, 11, 1, -7 }, // 0x78 'x'
{ 809, 9, 11, 11, 1, -7 }, // 0x79 'y'
{ 822, 7, 8, 11, 2, -7 }, // 0x7A 'z'
{ 829, 3, 13, 11, 4, -10 }, // 0x7B '{'
{ 834, 1, 13, 11, 5, -10 }, // 0x7C '|'
{ 836, 3, 13, 11, 4, -10 }, // 0x7D '}'
{ 841, 7, 3, 11, 2, -6 } }; // 0x7E '~'
const GFXfont FreeMono9pt7b PROGMEM = {
(uint8_t *)FreeMono9pt7bBitmaps,
(GFXglyph *)FreeMono9pt7bGlyphs,
0x20, 0x7E, 18 };
// Approx. 1516 bytes

270
libraries/fonts/FreeSans12pt7b.h Executable file
View File

@ -0,0 +1,270 @@
const uint8_t FreeSans12pt7bBitmaps[] PROGMEM = {
0xFF, 0xFF, 0xFF, 0xF0, 0xF0, 0xCF, 0x3C, 0xF3, 0x8A, 0x20, 0x06, 0x30,
0x31, 0x03, 0x18, 0x18, 0xC7, 0xFF, 0xBF, 0xFC, 0x31, 0x03, 0x18, 0x18,
0xC7, 0xFF, 0xBF, 0xFC, 0x31, 0x01, 0x18, 0x18, 0xC0, 0xC6, 0x06, 0x30,
0x04, 0x03, 0xE1, 0xFF, 0x72, 0x6C, 0x47, 0x88, 0xF1, 0x07, 0x20, 0x7E,
0x03, 0xF0, 0x17, 0x02, 0x3C, 0x47, 0x88, 0xF1, 0x1B, 0x26, 0x7F, 0xC3,
0xE0, 0x10, 0x02, 0x00, 0x00, 0x06, 0x03, 0xC0, 0x40, 0x7E, 0x0C, 0x0E,
0x70, 0x80, 0xC3, 0x18, 0x0C, 0x31, 0x00, 0xE7, 0x30, 0x07, 0xE6, 0x00,
0x3C, 0x40, 0x00, 0x0C, 0x7C, 0x00, 0x8F, 0xE0, 0x19, 0xC7, 0x01, 0x18,
0x30, 0x31, 0x83, 0x02, 0x1C, 0x70, 0x40, 0xFE, 0x04, 0x07, 0xC0, 0x0F,
0x00, 0x7E, 0x03, 0x9C, 0x0C, 0x30, 0x30, 0xC0, 0xE7, 0x01, 0xF8, 0x03,
0x80, 0x3E, 0x01, 0xCC, 0x6E, 0x19, 0xB0, 0x7C, 0xC0, 0xF3, 0x03, 0xCE,
0x1F, 0x9F, 0xE6, 0x1E, 0x1C, 0xFF, 0xA0, 0x08, 0x8C, 0x66, 0x31, 0x98,
0xC6, 0x31, 0x8C, 0x63, 0x08, 0x63, 0x08, 0x61, 0x0C, 0x20, 0x82, 0x18,
0xC3, 0x18, 0xC3, 0x18, 0xC6, 0x31, 0x8C, 0x62, 0x31, 0x88, 0xC4, 0x62,
0x00, 0x10, 0x23, 0x5B, 0xE3, 0x8D, 0x91, 0x00, 0x0C, 0x03, 0x00, 0xC0,
0x30, 0xFF, 0xFF, 0xF0, 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0xF5, 0x60,
0xFF, 0xF0, 0xF0, 0x02, 0x0C, 0x10, 0x20, 0xC1, 0x02, 0x0C, 0x10, 0x20,
0xC1, 0x02, 0x0C, 0x10, 0x20, 0xC1, 0x00, 0x1F, 0x07, 0xF1, 0xC7, 0x30,
0x6E, 0x0F, 0x80, 0xF0, 0x1E, 0x03, 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x3C,
0x0E, 0xC1, 0x9C, 0x71, 0xFC, 0x1F, 0x00, 0x08, 0xCF, 0xFF, 0x8C, 0x63,
0x18, 0xC6, 0x31, 0x8C, 0x63, 0x18, 0x1F, 0x0F, 0xF9, 0x87, 0x60, 0x7C,
0x06, 0x00, 0xC0, 0x18, 0x07, 0x01, 0xC0, 0xF0, 0x78, 0x1C, 0x06, 0x00,
0x80, 0x30, 0x07, 0xFF, 0xFF, 0xE0, 0x3F, 0x0F, 0xF3, 0x87, 0x60, 0x6C,
0x0C, 0x01, 0x80, 0x70, 0x7C, 0x0F, 0x80, 0x18, 0x01, 0x80, 0x3C, 0x07,
0x80, 0xD8, 0x73, 0xFC, 0x1F, 0x00, 0x01, 0x80, 0x70, 0x0E, 0x03, 0xC0,
0xD8, 0x1B, 0x06, 0x61, 0x8C, 0x21, 0x8C, 0x33, 0x06, 0x7F, 0xFF, 0xFE,
0x03, 0x00, 0x60, 0x0C, 0x01, 0x80, 0x3F, 0xCF, 0xF9, 0x80, 0x30, 0x06,
0x00, 0xDE, 0x1F, 0xE7, 0x0E, 0x00, 0xE0, 0x0C, 0x01, 0x80, 0x30, 0x07,
0x81, 0xF8, 0x73, 0xFC, 0x1F, 0x00, 0x0F, 0x07, 0xF9, 0xC3, 0x30, 0x74,
0x01, 0x80, 0x33, 0xC7, 0xFE, 0xF0, 0xDC, 0x1F, 0x01, 0xE0, 0x3C, 0x06,
0xC1, 0xDC, 0x71, 0xFC, 0x1F, 0x00, 0xFF, 0xFF, 0xFC, 0x01, 0x00, 0x60,
0x18, 0x02, 0x00, 0xC0, 0x30, 0x06, 0x01, 0x80, 0x30, 0x04, 0x01, 0x80,
0x30, 0x06, 0x01, 0x80, 0x30, 0x00, 0x1F, 0x07, 0xF1, 0xC7, 0x30, 0x66,
0x0C, 0xC1, 0x8C, 0x61, 0xFC, 0x3F, 0x8E, 0x3B, 0x01, 0xE0, 0x3C, 0x07,
0x80, 0xD8, 0x31, 0xFC, 0x1F, 0x00, 0x1F, 0x07, 0xF1, 0xC7, 0x70, 0x6C,
0x07, 0x80, 0xF0, 0x1E, 0x07, 0x61, 0xEF, 0xFC, 0x79, 0x80, 0x30, 0x05,
0x81, 0x98, 0x73, 0xFC, 0x1E, 0x00, 0xF0, 0x00, 0x03, 0xC0, 0xF0, 0x00,
0x0F, 0x56, 0x00, 0x00, 0x07, 0x01, 0xE0, 0xF8, 0x3C, 0x0F, 0x00, 0xE0,
0x07, 0xC0, 0x0F, 0x00, 0x3C, 0x00, 0xF0, 0x01, 0xFF, 0xFF, 0xFF, 0x00,
0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x0E, 0x00, 0x78, 0x01, 0xF0, 0x07,
0xC0, 0x0F, 0x00, 0x70, 0x1E, 0x0F, 0x03, 0xC0, 0xF0, 0x08, 0x00, 0x1F,
0x1F, 0xEE, 0x1B, 0x03, 0xC0, 0xC0, 0x30, 0x0C, 0x06, 0x03, 0x81, 0xC0,
0xE0, 0x30, 0x0C, 0x03, 0x00, 0x00, 0x00, 0x0C, 0x03, 0x00, 0x00, 0xFE,
0x00, 0x0F, 0xFE, 0x00, 0xF0, 0x3E, 0x07, 0x00, 0x3C, 0x38, 0x00, 0x30,
0xC1, 0xE0, 0x66, 0x0F, 0xD9, 0xD8, 0x61, 0xC3, 0xC3, 0x07, 0x0F, 0x1C,
0x1C, 0x3C, 0x60, 0x60, 0xF1, 0x81, 0x83, 0xC6, 0x06, 0x1B, 0x18, 0x38,
0xEE, 0x71, 0xE7, 0x18, 0xFD, 0xF8, 0x71, 0xE7, 0xC0, 0xE0, 0x00, 0x01,
0xE0, 0x00, 0x01, 0xFF, 0xC0, 0x01, 0xFC, 0x00, 0x03, 0xC0, 0x03, 0xC0,
0x03, 0xC0, 0x07, 0xE0, 0x06, 0x60, 0x06, 0x60, 0x0E, 0x70, 0x0C, 0x30,
0x0C, 0x30, 0x1C, 0x38, 0x18, 0x18, 0x1F, 0xF8, 0x3F, 0xFC, 0x30, 0x1C,
0x30, 0x0C, 0x70, 0x0E, 0x60, 0x06, 0x60, 0x06, 0xFF, 0xC7, 0xFF, 0x30,
0x19, 0x80, 0x6C, 0x03, 0x60, 0x1B, 0x00, 0xD8, 0x0C, 0xFF, 0xC7, 0xFF,
0x30, 0x0D, 0x80, 0x3C, 0x01, 0xE0, 0x0F, 0x00, 0x78, 0x06, 0xFF, 0xF7,
0xFE, 0x00, 0x07, 0xE0, 0x3F, 0xF0, 0xE0, 0x73, 0x80, 0x66, 0x00, 0x6C,
0x00, 0x30, 0x00, 0x60, 0x00, 0xC0, 0x01, 0x80, 0x03, 0x00, 0x06, 0x00,
0x06, 0x00, 0x6C, 0x00, 0xDC, 0x03, 0x1E, 0x0E, 0x1F, 0xF8, 0x0F, 0xC0,
0xFF, 0x83, 0xFF, 0x8C, 0x07, 0x30, 0x0E, 0xC0, 0x1B, 0x00, 0x7C, 0x00,
0xF0, 0x03, 0xC0, 0x0F, 0x00, 0x3C, 0x00, 0xF0, 0x03, 0xC0, 0x1F, 0x00,
0x6C, 0x03, 0xB0, 0x1C, 0xFF, 0xE3, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xC0,
0x0C, 0x00, 0xC0, 0x0C, 0x00, 0xC0, 0x0C, 0x00, 0xFF, 0xEF, 0xFE, 0xC0,
0x0C, 0x00, 0xC0, 0x0C, 0x00, 0xC0, 0x0C, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0x00, 0x60, 0x0C, 0x01, 0x80, 0x30, 0x06, 0x00, 0xFF, 0xDF,
0xFB, 0x00, 0x60, 0x0C, 0x01, 0x80, 0x30, 0x06, 0x00, 0xC0, 0x18, 0x00,
0x07, 0xF0, 0x1F, 0xFC, 0x3C, 0x1E, 0x70, 0x06, 0x60, 0x03, 0xE0, 0x00,
0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x7F, 0xC0, 0x7F, 0xC0, 0x03, 0xC0, 0x03,
0x60, 0x03, 0x60, 0x07, 0x30, 0x0F, 0x3C, 0x1F, 0x1F, 0xFB, 0x07, 0xE1,
0xC0, 0x1E, 0x00, 0xF0, 0x07, 0x80, 0x3C, 0x01, 0xE0, 0x0F, 0x00, 0x78,
0x03, 0xFF, 0xFF, 0xFF, 0xF0, 0x07, 0x80, 0x3C, 0x01, 0xE0, 0x0F, 0x00,
0x78, 0x03, 0xC0, 0x1E, 0x00, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x01,
0x80, 0xC0, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x03, 0x01, 0x80, 0xC0, 0x60,
0x3C, 0x1E, 0x0F, 0x07, 0xC7, 0x7F, 0x1F, 0x00, 0xC0, 0x3B, 0x01, 0xCC,
0x0E, 0x30, 0x70, 0xC3, 0x83, 0x1C, 0x0C, 0xE0, 0x33, 0x80, 0xDE, 0x03,
0xDC, 0x0E, 0x38, 0x30, 0x60, 0xC1, 0xC3, 0x03, 0x8C, 0x06, 0x30, 0x1C,
0xC0, 0x3B, 0x00, 0x60, 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x30, 0x0C,
0x03, 0x00, 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x30, 0x0C, 0x03, 0x00,
0xFF, 0xFF, 0xF0, 0xE0, 0x07, 0xE0, 0x07, 0xF0, 0x0F, 0xF0, 0x0F, 0xD0,
0x0F, 0xD8, 0x1B, 0xD8, 0x1B, 0xD8, 0x1B, 0xCC, 0x33, 0xCC, 0x33, 0xCC,
0x33, 0xC6, 0x63, 0xC6, 0x63, 0xC6, 0x63, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3,
0xC3, 0xC1, 0x83, 0xE0, 0x1F, 0x00, 0xFC, 0x07, 0xE0, 0x3D, 0x81, 0xEE,
0x0F, 0x30, 0x79, 0xC3, 0xC6, 0x1E, 0x18, 0xF0, 0xE7, 0x83, 0x3C, 0x1D,
0xE0, 0x6F, 0x01, 0xF8, 0x0F, 0xC0, 0x3E, 0x01, 0xC0, 0x03, 0xE0, 0x0F,
0xFC, 0x0F, 0x07, 0x86, 0x00, 0xC6, 0x00, 0x33, 0x00, 0x1B, 0x00, 0x07,
0x80, 0x03, 0xC0, 0x01, 0xE0, 0x00, 0xF0, 0x00, 0x78, 0x00, 0x36, 0x00,
0x33, 0x00, 0x18, 0xC0, 0x18, 0x78, 0x3C, 0x1F, 0xFC, 0x03, 0xF8, 0x00,
0xFF, 0x8F, 0xFE, 0xC0, 0x6C, 0x03, 0xC0, 0x3C, 0x03, 0xC0, 0x3C, 0x07,
0xFF, 0xEF, 0xFC, 0xC0, 0x0C, 0x00, 0xC0, 0x0C, 0x00, 0xC0, 0x0C, 0x00,
0xC0, 0x0C, 0x00, 0x03, 0xE0, 0x0F, 0xFC, 0x0F, 0x07, 0x86, 0x00, 0xC6,
0x00, 0x33, 0x00, 0x1B, 0x00, 0x07, 0x80, 0x03, 0xC0, 0x01, 0xE0, 0x00,
0xF0, 0x00, 0x78, 0x00, 0x36, 0x00, 0x33, 0x01, 0x98, 0xC0, 0xFC, 0x78,
0x3C, 0x1F, 0xFF, 0x03, 0xF9, 0x80, 0x00, 0x40, 0xFF, 0xC3, 0xFF, 0xCC,
0x03, 0xB0, 0x06, 0xC0, 0x1B, 0x00, 0x6C, 0x01, 0xB0, 0x0C, 0xFF, 0xE3,
0xFF, 0xCC, 0x03, 0xB0, 0x06, 0xC0, 0x1B, 0x00, 0x6C, 0x01, 0xB0, 0x06,
0xC0, 0x1B, 0x00, 0x70, 0x0F, 0xE0, 0x7F, 0xC3, 0x83, 0x9C, 0x07, 0x60,
0x0D, 0x80, 0x06, 0x00, 0x1E, 0x00, 0x3F, 0x80, 0x3F, 0xC0, 0x0F, 0x80,
0x07, 0xC0, 0x0F, 0x00, 0x3E, 0x00, 0xDE, 0x0E, 0x3F, 0xF0, 0x3F, 0x80,
0xFF, 0xFF, 0xFF, 0x06, 0x00, 0x60, 0x06, 0x00, 0x60, 0x06, 0x00, 0x60,
0x06, 0x00, 0x60, 0x06, 0x00, 0x60, 0x06, 0x00, 0x60, 0x06, 0x00, 0x60,
0x06, 0x00, 0x60, 0xC0, 0x1E, 0x00, 0xF0, 0x07, 0x80, 0x3C, 0x01, 0xE0,
0x0F, 0x00, 0x78, 0x03, 0xC0, 0x1E, 0x00, 0xF0, 0x07, 0x80, 0x3C, 0x01,
0xE0, 0x0F, 0x80, 0xEE, 0x0E, 0x3F, 0xE0, 0x7C, 0x00, 0x60, 0x06, 0xC0,
0x1D, 0xC0, 0x31, 0x80, 0x63, 0x01, 0xC7, 0x03, 0x06, 0x06, 0x0C, 0x1C,
0x1C, 0x30, 0x18, 0x60, 0x31, 0xC0, 0x73, 0x00, 0x66, 0x00, 0xDC, 0x01,
0xF0, 0x01, 0xE0, 0x03, 0xC0, 0x07, 0x00, 0xE0, 0x30, 0x1D, 0x80, 0xE0,
0x76, 0x07, 0x81, 0xD8, 0x1E, 0x06, 0x70, 0x7C, 0x18, 0xC1, 0xB0, 0xE3,
0x0C, 0xC3, 0x8C, 0x33, 0x0C, 0x38, 0xC6, 0x30, 0x67, 0x18, 0xC1, 0x98,
0x67, 0x06, 0x61, 0xD8, 0x1D, 0x83, 0x60, 0x3C, 0x0D, 0x80, 0xF0, 0x3E,
0x03, 0xC0, 0x70, 0x0F, 0x01, 0xC0, 0x18, 0x07, 0x00, 0x70, 0x0E, 0x60,
0x38, 0xE0, 0x60, 0xE1, 0xC0, 0xC3, 0x01, 0xCC, 0x01, 0xF8, 0x01, 0xE0,
0x03, 0x80, 0x07, 0x80, 0x1F, 0x00, 0x33, 0x00, 0xE7, 0x03, 0x86, 0x06,
0x0E, 0x1C, 0x0E, 0x70, 0x0C, 0xC0, 0x1C, 0x60, 0x06, 0x70, 0x0E, 0x30,
0x1C, 0x38, 0x18, 0x1C, 0x38, 0x0C, 0x30, 0x0E, 0x70, 0x06, 0x60, 0x03,
0xC0, 0x03, 0xC0, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01,
0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0xFF, 0xFF, 0xFF, 0xC0, 0x0E,
0x00, 0xE0, 0x0E, 0x00, 0x60, 0x07, 0x00, 0x70, 0x07, 0x00, 0x30, 0x03,
0x80, 0x38, 0x03, 0x80, 0x18, 0x01, 0xC0, 0x1C, 0x00, 0xFF, 0xFF, 0xFF,
0xC0, 0xFF, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCF,
0xF0, 0x81, 0x81, 0x02, 0x06, 0x04, 0x08, 0x18, 0x10, 0x20, 0x60, 0x40,
0x81, 0x81, 0x02, 0x06, 0x04, 0xFF, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33,
0x33, 0x33, 0x33, 0x3F, 0xF0, 0x0C, 0x0E, 0x05, 0x86, 0xC3, 0x21, 0x19,
0x8C, 0x83, 0xC1, 0x80, 0xFF, 0xFE, 0xE3, 0x8C, 0x30, 0x3F, 0x07, 0xF8,
0xE1, 0xCC, 0x0C, 0x00, 0xC0, 0x1C, 0x3F, 0xCF, 0x8C, 0xC0, 0xCC, 0x0C,
0xE3, 0xC7, 0xEF, 0x3C, 0x70, 0xC0, 0x0C, 0x00, 0xC0, 0x0C, 0x00, 0xC0,
0x0C, 0xF8, 0xDF, 0xCF, 0x0E, 0xE0, 0x7C, 0x03, 0xC0, 0x3C, 0x03, 0xC0,
0x3C, 0x03, 0xE0, 0x6F, 0x0E, 0xDF, 0xCC, 0xF8, 0x1F, 0x0F, 0xE7, 0x1B,
0x83, 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x38, 0x37, 0x1C, 0xFE, 0x1F,
0x00, 0x00, 0x60, 0x0C, 0x01, 0x80, 0x30, 0x06, 0x3C, 0xCF, 0xFB, 0x8F,
0xE0, 0xF8, 0x0F, 0x01, 0xE0, 0x3C, 0x07, 0x80, 0xF8, 0x3B, 0x8F, 0x3F,
0x63, 0xCC, 0x1F, 0x07, 0xF1, 0xC7, 0x70, 0x3C, 0x07, 0xFF, 0xFF, 0xFE,
0x00, 0xC0, 0x1C, 0x0D, 0xC3, 0x1F, 0xE1, 0xF0, 0x3B, 0xD8, 0xC6, 0x7F,
0xEC, 0x63, 0x18, 0xC6, 0x31, 0x8C, 0x63, 0x00, 0x1E, 0x67, 0xFD, 0xC7,
0xF0, 0x7C, 0x07, 0x80, 0xF0, 0x1E, 0x03, 0xC0, 0x7C, 0x1D, 0xC7, 0x9F,
0xB1, 0xE6, 0x00, 0xC0, 0x3E, 0x0E, 0x7F, 0xC7, 0xE0, 0xC0, 0x30, 0x0C,
0x03, 0x00, 0xC0, 0x33, 0xCD, 0xFB, 0xC7, 0xE0, 0xF0, 0x3C, 0x0F, 0x03,
0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x30, 0xF0, 0x3F, 0xFF, 0xFF,
0xF0, 0x33, 0x00, 0x03, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x3F,
0xE0, 0xC0, 0x18, 0x03, 0x00, 0x60, 0x0C, 0x01, 0x83, 0x30, 0xC6, 0x30,
0xCC, 0x1B, 0x83, 0xF0, 0x77, 0x0C, 0x61, 0x8E, 0x30, 0xE6, 0x0C, 0xC1,
0xD8, 0x18, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xCF, 0x1F, 0x6F, 0xDF, 0xFC,
0x78, 0xFC, 0x18, 0x3C, 0x0C, 0x1E, 0x06, 0x0F, 0x03, 0x07, 0x81, 0x83,
0xC0, 0xC1, 0xE0, 0x60, 0xF0, 0x30, 0x78, 0x18, 0x3C, 0x0C, 0x18, 0xCF,
0x37, 0xEF, 0x1F, 0x83, 0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x3C,
0x0F, 0x03, 0xC0, 0xC0, 0x1F, 0x07, 0xF1, 0xC7, 0x70, 0x7C, 0x07, 0x80,
0xF0, 0x1E, 0x03, 0xC0, 0x7C, 0x1D, 0xC7, 0x1F, 0xC1, 0xF0, 0xCF, 0x8D,
0xFC, 0xF0, 0xEE, 0x06, 0xC0, 0x3C, 0x03, 0xC0, 0x3C, 0x03, 0xC0, 0x3E,
0x07, 0xF0, 0xEF, 0xFC, 0xCF, 0x8C, 0x00, 0xC0, 0x0C, 0x00, 0xC0, 0x00,
0x1E, 0x67, 0xFD, 0xC7, 0xF0, 0x7C, 0x07, 0x80, 0xF0, 0x1E, 0x03, 0xC0,
0x7C, 0x1D, 0xC7, 0x9F, 0xF1, 0xE6, 0x00, 0xC0, 0x18, 0x03, 0x00, 0x60,
0xCF, 0x7F, 0x38, 0xC3, 0x0C, 0x30, 0xC3, 0x0C, 0x30, 0xC0, 0x3E, 0x1F,
0xEE, 0x1B, 0x00, 0xC0, 0x3C, 0x07, 0xF0, 0x3E, 0x01, 0xF0, 0x3E, 0x1D,
0xFE, 0x3E, 0x00, 0x63, 0x19, 0xFF, 0xB1, 0x8C, 0x63, 0x18, 0xC6, 0x31,
0xE7, 0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0,
0xF0, 0x7E, 0x3D, 0xFB, 0x3C, 0xC0, 0xE0, 0x66, 0x06, 0x60, 0x67, 0x0C,
0x30, 0xC3, 0x0C, 0x39, 0x81, 0x98, 0x19, 0x81, 0xF0, 0x0F, 0x00, 0xE0,
0x0E, 0x00, 0xC1, 0xC1, 0xB0, 0xE1, 0xD8, 0x70, 0xCC, 0x2C, 0x66, 0x36,
0x31, 0x9B, 0x18, 0xCD, 0x98, 0x64, 0x6C, 0x16, 0x36, 0x0F, 0x1A, 0x07,
0x8F, 0x03, 0x83, 0x80, 0xC1, 0xC0, 0x60, 0xEE, 0x18, 0xC6, 0x0C, 0xC1,
0xF0, 0x1C, 0x01, 0x80, 0x78, 0x1B, 0x03, 0x30, 0xC7, 0x30, 0x66, 0x06,
0xE0, 0x6C, 0x0D, 0x83, 0x38, 0x63, 0x0C, 0x63, 0x0E, 0x60, 0xCC, 0x1B,
0x03, 0x60, 0x3C, 0x07, 0x00, 0xE0, 0x18, 0x03, 0x00, 0xE0, 0x78, 0x0E,
0x00, 0xFF, 0xFF, 0xF0, 0x18, 0x0C, 0x07, 0x03, 0x81, 0xC0, 0x60, 0x30,
0x18, 0x0E, 0x03, 0xFF, 0xFF, 0xC0, 0x19, 0xCC, 0x63, 0x18, 0xC6, 0x31,
0x99, 0x86, 0x18, 0xC6, 0x31, 0x8C, 0x63, 0x1C, 0x60, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFC, 0xC7, 0x18, 0xC6, 0x31, 0x8C, 0x63, 0x0C, 0x33, 0x31,
0x8C, 0x63, 0x18, 0xC6, 0x73, 0x00, 0x70, 0x3E, 0x09, 0xE4, 0x1F, 0x03,
0x80 };
const GFXglyph FreeSans12pt7bGlyphs[] PROGMEM = {
{ 0, 0, 0, 6, 0, 1 }, // 0x20 ' '
{ 0, 2, 18, 8, 3, -17 }, // 0x21 '!'
{ 5, 6, 6, 8, 1, -16 }, // 0x22 '"'
{ 10, 13, 16, 13, 0, -15 }, // 0x23 '#'
{ 36, 11, 20, 13, 1, -17 }, // 0x24 '$'
{ 64, 20, 17, 21, 1, -16 }, // 0x25 '%'
{ 107, 14, 17, 16, 1, -16 }, // 0x26 '&'
{ 137, 2, 6, 5, 1, -16 }, // 0x27 '''
{ 139, 5, 23, 8, 2, -17 }, // 0x28 '('
{ 154, 5, 23, 8, 1, -17 }, // 0x29 ')'
{ 169, 7, 7, 9, 1, -17 }, // 0x2A '*'
{ 176, 10, 11, 14, 2, -10 }, // 0x2B '+'
{ 190, 2, 6, 7, 2, -1 }, // 0x2C ','
{ 192, 6, 2, 8, 1, -7 }, // 0x2D '-'
{ 194, 2, 2, 6, 2, -1 }, // 0x2E '.'
{ 195, 7, 18, 7, 0, -17 }, // 0x2F '/'
{ 211, 11, 17, 13, 1, -16 }, // 0x30 '0'
{ 235, 5, 17, 13, 3, -16 }, // 0x31 '1'
{ 246, 11, 17, 13, 1, -16 }, // 0x32 '2'
{ 270, 11, 17, 13, 1, -16 }, // 0x33 '3'
{ 294, 11, 17, 13, 1, -16 }, // 0x34 '4'
{ 318, 11, 17, 13, 1, -16 }, // 0x35 '5'
{ 342, 11, 17, 13, 1, -16 }, // 0x36 '6'
{ 366, 11, 17, 13, 1, -16 }, // 0x37 '7'
{ 390, 11, 17, 13, 1, -16 }, // 0x38 '8'
{ 414, 11, 17, 13, 1, -16 }, // 0x39 '9'
{ 438, 2, 13, 6, 2, -12 }, // 0x3A ':'
{ 442, 2, 16, 6, 2, -11 }, // 0x3B ';'
{ 446, 12, 12, 14, 1, -11 }, // 0x3C '<'
{ 464, 12, 6, 14, 1, -8 }, // 0x3D '='
{ 473, 12, 12, 14, 1, -11 }, // 0x3E '>'
{ 491, 10, 18, 13, 2, -17 }, // 0x3F '?'
{ 514, 22, 21, 24, 1, -17 }, // 0x40 '@'
{ 572, 16, 18, 16, 0, -17 }, // 0x41 'A'
{ 608, 13, 18, 16, 2, -17 }, // 0x42 'B'
{ 638, 15, 18, 17, 1, -17 }, // 0x43 'C'
{ 672, 14, 18, 17, 2, -17 }, // 0x44 'D'
{ 704, 12, 18, 15, 2, -17 }, // 0x45 'E'
{ 731, 11, 18, 14, 2, -17 }, // 0x46 'F'
{ 756, 16, 18, 18, 1, -17 }, // 0x47 'G'
{ 792, 13, 18, 17, 2, -17 }, // 0x48 'H'
{ 822, 2, 18, 7, 2, -17 }, // 0x49 'I'
{ 827, 9, 18, 13, 1, -17 }, // 0x4A 'J'
{ 848, 14, 18, 16, 2, -17 }, // 0x4B 'K'
{ 880, 10, 18, 14, 2, -17 }, // 0x4C 'L'
{ 903, 16, 18, 20, 2, -17 }, // 0x4D 'M'
{ 939, 13, 18, 18, 2, -17 }, // 0x4E 'N'
{ 969, 17, 18, 19, 1, -17 }, // 0x4F 'O'
{ 1008, 12, 18, 16, 2, -17 }, // 0x50 'P'
{ 1035, 17, 19, 19, 1, -17 }, // 0x51 'Q'
{ 1076, 14, 18, 17, 2, -17 }, // 0x52 'R'
{ 1108, 14, 18, 16, 1, -17 }, // 0x53 'S'
{ 1140, 12, 18, 15, 1, -17 }, // 0x54 'T'
{ 1167, 13, 18, 17, 2, -17 }, // 0x55 'U'
{ 1197, 15, 18, 15, 0, -17 }, // 0x56 'V'
{ 1231, 22, 18, 22, 0, -17 }, // 0x57 'W'
{ 1281, 15, 18, 16, 0, -17 }, // 0x58 'X'
{ 1315, 16, 18, 16, 0, -17 }, // 0x59 'Y'
{ 1351, 13, 18, 15, 1, -17 }, // 0x5A 'Z'
{ 1381, 4, 23, 7, 2, -17 }, // 0x5B '['
{ 1393, 7, 18, 7, 0, -17 }, // 0x5C '\'
{ 1409, 4, 23, 7, 1, -17 }, // 0x5D ']'
{ 1421, 9, 9, 11, 1, -16 }, // 0x5E '^'
{ 1432, 15, 1, 13, -1, 4 }, // 0x5F '_'
{ 1434, 5, 4, 6, 1, -17 }, // 0x60 '`'
{ 1437, 12, 13, 13, 1, -12 }, // 0x61 'a'
{ 1457, 12, 18, 13, 1, -17 }, // 0x62 'b'
{ 1484, 10, 13, 12, 1, -12 }, // 0x63 'c'
{ 1501, 11, 18, 13, 1, -17 }, // 0x64 'd'
{ 1526, 11, 13, 13, 1, -12 }, // 0x65 'e'
{ 1544, 5, 18, 7, 1, -17 }, // 0x66 'f'
{ 1556, 11, 18, 13, 1, -12 }, // 0x67 'g'
{ 1581, 10, 18, 13, 1, -17 }, // 0x68 'h'
{ 1604, 2, 18, 5, 2, -17 }, // 0x69 'i'
{ 1609, 4, 23, 6, 0, -17 }, // 0x6A 'j'
{ 1621, 11, 18, 12, 1, -17 }, // 0x6B 'k'
{ 1646, 2, 18, 5, 1, -17 }, // 0x6C 'l'
{ 1651, 17, 13, 19, 1, -12 }, // 0x6D 'm'
{ 1679, 10, 13, 13, 1, -12 }, // 0x6E 'n'
{ 1696, 11, 13, 13, 1, -12 }, // 0x6F 'o'
{ 1714, 12, 17, 13, 1, -12 }, // 0x70 'p'
{ 1740, 11, 17, 13, 1, -12 }, // 0x71 'q'
{ 1764, 6, 13, 8, 1, -12 }, // 0x72 'r'
{ 1774, 10, 13, 12, 1, -12 }, // 0x73 's'
{ 1791, 5, 16, 7, 1, -15 }, // 0x74 't'
{ 1801, 10, 13, 13, 1, -12 }, // 0x75 'u'
{ 1818, 12, 13, 12, 0, -12 }, // 0x76 'v'
{ 1838, 17, 13, 17, 0, -12 }, // 0x77 'w'
{ 1866, 11, 13, 11, 0, -12 }, // 0x78 'x'
{ 1884, 11, 18, 11, 0, -12 }, // 0x79 'y'
{ 1909, 10, 13, 12, 1, -12 }, // 0x7A 'z'
{ 1926, 5, 23, 8, 1, -17 }, // 0x7B '{'
{ 1941, 2, 23, 6, 2, -17 }, // 0x7C '|'
{ 1947, 5, 23, 8, 2, -17 }, // 0x7D '}'
{ 1962, 10, 5, 12, 1, -10 } }; // 0x7E '~'
const GFXfont FreeSans12pt7b PROGMEM = {
(uint8_t *)FreeSans12pt7bBitmaps,
(GFXglyph *)FreeSans12pt7bGlyphs,
0x20, 0x7E, 29 };
// Approx. 2641 bytes

201
libraries/fonts/FreeSans9pt7b.h Executable file
View File

@ -0,0 +1,201 @@
const uint8_t FreeSans9pt7bBitmaps[] PROGMEM = {
0xFF, 0xFF, 0xF8, 0xC0, 0xDE, 0xF7, 0x20, 0x09, 0x86, 0x41, 0x91, 0xFF,
0x13, 0x04, 0xC3, 0x20, 0xC8, 0xFF, 0x89, 0x82, 0x61, 0x90, 0x10, 0x1F,
0x14, 0xDA, 0x3D, 0x1E, 0x83, 0x40, 0x78, 0x17, 0x08, 0xF4, 0x7A, 0x35,
0x33, 0xF0, 0x40, 0x20, 0x38, 0x10, 0xEC, 0x20, 0xC6, 0x20, 0xC6, 0x40,
0xC6, 0x40, 0x6C, 0x80, 0x39, 0x00, 0x01, 0x3C, 0x02, 0x77, 0x02, 0x63,
0x04, 0x63, 0x04, 0x77, 0x08, 0x3C, 0x0E, 0x06, 0x60, 0xCC, 0x19, 0x81,
0xE0, 0x18, 0x0F, 0x03, 0x36, 0xC2, 0xD8, 0x73, 0x06, 0x31, 0xE3, 0xC4,
0xFE, 0x13, 0x26, 0x6C, 0xCC, 0xCC, 0xC4, 0x66, 0x23, 0x10, 0x8C, 0x46,
0x63, 0x33, 0x33, 0x32, 0x66, 0x4C, 0x80, 0x25, 0x7E, 0xA5, 0x00, 0x30,
0xC3, 0x3F, 0x30, 0xC3, 0x0C, 0xD6, 0xF0, 0xC0, 0x08, 0x44, 0x21, 0x10,
0x84, 0x42, 0x11, 0x08, 0x00, 0x3C, 0x66, 0x42, 0xC3, 0xC3, 0xC3, 0xC3,
0xC3, 0xC3, 0xC3, 0x42, 0x66, 0x3C, 0x11, 0x3F, 0x33, 0x33, 0x33, 0x33,
0x30, 0x3E, 0x31, 0xB0, 0x78, 0x30, 0x18, 0x1C, 0x1C, 0x1C, 0x18, 0x18,
0x10, 0x08, 0x07, 0xF8, 0x3C, 0x66, 0xC3, 0xC3, 0x03, 0x06, 0x1C, 0x07,
0x03, 0xC3, 0xC3, 0x66, 0x3C, 0x0C, 0x18, 0x71, 0x62, 0xC9, 0xA3, 0x46,
0xFE, 0x18, 0x30, 0x60, 0xC0, 0x7F, 0x20, 0x10, 0x08, 0x08, 0x07, 0xF3,
0x8C, 0x03, 0x01, 0x80, 0xF0, 0x6C, 0x63, 0xE0, 0x1E, 0x31, 0x98, 0x78,
0x0C, 0x06, 0xF3, 0x8D, 0x83, 0xC1, 0xE0, 0xD0, 0x6C, 0x63, 0xE0, 0xFF,
0x03, 0x02, 0x06, 0x04, 0x0C, 0x08, 0x18, 0x18, 0x18, 0x10, 0x30, 0x30,
0x3E, 0x31, 0xB0, 0x78, 0x3C, 0x1B, 0x18, 0xF8, 0xC6, 0xC1, 0xE0, 0xF0,
0x6C, 0x63, 0xE0, 0x3C, 0x66, 0xC2, 0xC3, 0xC3, 0xC3, 0x67, 0x3B, 0x03,
0x03, 0xC2, 0x66, 0x3C, 0xC0, 0x00, 0x30, 0xC0, 0x00, 0x00, 0x64, 0xA0,
0x00, 0x81, 0xC7, 0x8E, 0x0C, 0x07, 0x80, 0x70, 0x0E, 0x01, 0x80, 0xFF,
0x80, 0x00, 0x1F, 0xF0, 0x00, 0x70, 0x0E, 0x01, 0xC0, 0x18, 0x38, 0x71,
0xC0, 0x80, 0x00, 0x3E, 0x31, 0xB0, 0x78, 0x30, 0x18, 0x18, 0x38, 0x18,
0x18, 0x0C, 0x00, 0x00, 0x01, 0x80, 0x03, 0xF0, 0x06, 0x0E, 0x06, 0x01,
0x86, 0x00, 0x66, 0x1D, 0xBB, 0x31, 0xCF, 0x18, 0xC7, 0x98, 0x63, 0xCC,
0x31, 0xE6, 0x11, 0xB3, 0x99, 0xCC, 0xF7, 0x86, 0x00, 0x01, 0x80, 0x00,
0x70, 0x40, 0x0F, 0xE0, 0x06, 0x00, 0xF0, 0x0F, 0x00, 0x90, 0x19, 0x81,
0x98, 0x10, 0x83, 0x0C, 0x3F, 0xC2, 0x04, 0x60, 0x66, 0x06, 0xC0, 0x30,
0xFF, 0x18, 0x33, 0x03, 0x60, 0x6C, 0x0D, 0x83, 0x3F, 0xC6, 0x06, 0xC0,
0x78, 0x0F, 0x01, 0xE0, 0x6F, 0xF8, 0x1F, 0x86, 0x19, 0x81, 0xA0, 0x3C,
0x01, 0x80, 0x30, 0x06, 0x00, 0xC0, 0x68, 0x0D, 0x83, 0x18, 0x61, 0xF0,
0xFF, 0x18, 0x33, 0x03, 0x60, 0x3C, 0x07, 0x80, 0xF0, 0x1E, 0x03, 0xC0,
0x78, 0x0F, 0x03, 0x60, 0xCF, 0xF0, 0xFF, 0xE0, 0x30, 0x18, 0x0C, 0x06,
0x03, 0xFD, 0x80, 0xC0, 0x60, 0x30, 0x18, 0x0F, 0xF8, 0xFF, 0xC0, 0xC0,
0xC0, 0xC0, 0xC0, 0xFE, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0x0F, 0x83,
0x0E, 0x60, 0x66, 0x03, 0xC0, 0x0C, 0x00, 0xC1, 0xFC, 0x03, 0xC0, 0x36,
0x03, 0x60, 0x73, 0x0F, 0x0F, 0x10, 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x3C,
0x07, 0x80, 0xFF, 0xFE, 0x03, 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x3C, 0x06,
0xFF, 0xFF, 0xFF, 0xC0, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC1, 0x83, 0x07,
0x8F, 0x1E, 0x27, 0x80, 0xC0, 0xD8, 0x33, 0x0C, 0x63, 0x0C, 0xC1, 0xB8,
0x3F, 0x07, 0x30, 0xC3, 0x18, 0x63, 0x06, 0x60, 0x6C, 0x0C, 0xC0, 0xC0,
0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xFF, 0xE0,
0x3F, 0x01, 0xFC, 0x1F, 0xE0, 0xFD, 0x05, 0xEC, 0x6F, 0x63, 0x79, 0x13,
0xCD, 0x9E, 0x6C, 0xF1, 0x47, 0x8E, 0x3C, 0x71, 0x80, 0xE0, 0x7C, 0x0F,
0xC1, 0xE8, 0x3D, 0x87, 0x98, 0xF1, 0x1E, 0x33, 0xC3, 0x78, 0x6F, 0x07,
0xE0, 0x7C, 0x0E, 0x0F, 0x81, 0x83, 0x18, 0x0C, 0xC0, 0x6C, 0x01, 0xE0,
0x0F, 0x00, 0x78, 0x03, 0xC0, 0x1B, 0x01, 0x98, 0x0C, 0x60, 0xC0, 0xF8,
0x00, 0xFF, 0x30, 0x6C, 0x0F, 0x03, 0xC0, 0xF0, 0x6F, 0xF3, 0x00, 0xC0,
0x30, 0x0C, 0x03, 0x00, 0xC0, 0x00, 0x0F, 0x81, 0x83, 0x18, 0x0C, 0xC0,
0x6C, 0x01, 0xE0, 0x0F, 0x00, 0x78, 0x03, 0xC0, 0x1B, 0x01, 0x98, 0x6C,
0x60, 0xC0, 0xFB, 0x00, 0x08, 0xFF, 0x8C, 0x0E, 0xC0, 0x6C, 0x06, 0xC0,
0x6C, 0x0C, 0xFF, 0x8C, 0x0E, 0xC0, 0x6C, 0x06, 0xC0, 0x6C, 0x06, 0xC0,
0x70, 0x3F, 0x18, 0x6C, 0x0F, 0x03, 0xC0, 0x1E, 0x01, 0xF0, 0x0E, 0x00,
0xF0, 0x3C, 0x0D, 0x86, 0x3F, 0x00, 0xFF, 0x86, 0x03, 0x01, 0x80, 0xC0,
0x60, 0x30, 0x18, 0x0C, 0x06, 0x03, 0x01, 0x80, 0xC0, 0xC0, 0x78, 0x0F,
0x01, 0xE0, 0x3C, 0x07, 0x80, 0xF0, 0x1E, 0x03, 0xC0, 0x78, 0x0F, 0x01,
0xB0, 0x61, 0xF0, 0xC0, 0x6C, 0x0D, 0x81, 0x10, 0x63, 0x0C, 0x61, 0x04,
0x60, 0xCC, 0x19, 0x01, 0x60, 0x3C, 0x07, 0x00, 0x60, 0xC1, 0x81, 0x30,
0xE1, 0x98, 0x70, 0xCC, 0x28, 0x66, 0x26, 0x21, 0x13, 0x30, 0xC8, 0x98,
0x6C, 0x4C, 0x14, 0x34, 0x0A, 0x1A, 0x07, 0x07, 0x03, 0x03, 0x80, 0x81,
0x80, 0x60, 0x63, 0x0C, 0x30, 0xC1, 0x98, 0x0F, 0x00, 0xE0, 0x06, 0x00,
0xF0, 0x19, 0x01, 0x98, 0x30, 0xC6, 0x0E, 0x60, 0x60, 0xC0, 0x36, 0x06,
0x30, 0xC3, 0x0C, 0x19, 0x81, 0xD8, 0x0F, 0x00, 0x60, 0x06, 0x00, 0x60,
0x06, 0x00, 0x60, 0x06, 0x00, 0xFF, 0xC0, 0x60, 0x30, 0x0C, 0x06, 0x03,
0x01, 0xC0, 0x60, 0x30, 0x18, 0x06, 0x03, 0x00, 0xFF, 0xC0, 0xFB, 0x6D,
0xB6, 0xDB, 0x6D, 0xB6, 0xE0, 0x84, 0x10, 0x84, 0x10, 0x84, 0x10, 0x84,
0x10, 0x80, 0xED, 0xB6, 0xDB, 0x6D, 0xB6, 0xDB, 0xE0, 0x30, 0x60, 0xA2,
0x44, 0xD8, 0xA1, 0x80, 0xFF, 0xC0, 0xC6, 0x30, 0x7E, 0x71, 0xB0, 0xC0,
0x60, 0xF3, 0xDB, 0x0D, 0x86, 0xC7, 0x3D, 0xC0, 0xC0, 0x60, 0x30, 0x1B,
0xCE, 0x36, 0x0F, 0x07, 0x83, 0xC1, 0xE0, 0xF0, 0x7C, 0x6D, 0xE0, 0x3C,
0x66, 0xC3, 0xC0, 0xC0, 0xC0, 0xC0, 0xC3, 0x66, 0x3C, 0x03, 0x03, 0x03,
0x3B, 0x67, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x67, 0x3B, 0x3C, 0x66,
0xC3, 0xC3, 0xFF, 0xC0, 0xC0, 0xC3, 0x66, 0x3C, 0x36, 0x6F, 0x66, 0x66,
0x66, 0x66, 0x60, 0x3B, 0x67, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x67,
0x3B, 0x03, 0x03, 0xC6, 0x7C, 0xC0, 0xC0, 0xC0, 0xDE, 0xE3, 0xC3, 0xC3,
0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xFF, 0xFF, 0xC0, 0x30, 0x03,
0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0xE0, 0xC0, 0x60, 0x30, 0x18, 0x4C,
0x46, 0x63, 0x61, 0xF0, 0xEC, 0x62, 0x31, 0x98, 0x6C, 0x30, 0xFF, 0xFF,
0xFF, 0xC0, 0xDE, 0xF7, 0x1C, 0xF0, 0xC7, 0x86, 0x3C, 0x31, 0xE1, 0x8F,
0x0C, 0x78, 0x63, 0xC3, 0x1E, 0x18, 0xC0, 0xDE, 0xE3, 0xC3, 0xC3, 0xC3,
0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x3C, 0x66, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3,
0xC3, 0x66, 0x3C, 0xDE, 0x71, 0xB0, 0x78, 0x3C, 0x1E, 0x0F, 0x07, 0x83,
0xE3, 0x6F, 0x30, 0x18, 0x0C, 0x00, 0x3B, 0x67, 0xC3, 0xC3, 0xC3, 0xC3,
0xC3, 0xC3, 0x67, 0x3B, 0x03, 0x03, 0x03, 0xDF, 0x31, 0x8C, 0x63, 0x18,
0xC6, 0x00, 0x3E, 0xE3, 0xC0, 0xC0, 0xE0, 0x3C, 0x07, 0xC3, 0xE3, 0x7E,
0x66, 0xF6, 0x66, 0x66, 0x66, 0x67, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3,
0xC3, 0xC3, 0xC7, 0x7B, 0xC1, 0xA0, 0x98, 0xCC, 0x42, 0x21, 0xB0, 0xD0,
0x28, 0x1C, 0x0C, 0x00, 0xC6, 0x1E, 0x38, 0x91, 0xC4, 0xCA, 0x66, 0xD3,
0x16, 0xD0, 0xA6, 0x87, 0x1C, 0x38, 0xC0, 0xC6, 0x00, 0x43, 0x62, 0x36,
0x1C, 0x18, 0x1C, 0x3C, 0x26, 0x62, 0x43, 0xC1, 0x21, 0x98, 0xCC, 0x42,
0x61, 0xB0, 0xD0, 0x38, 0x1C, 0x0C, 0x06, 0x03, 0x01, 0x03, 0x00, 0xFE,
0x0C, 0x30, 0xC1, 0x86, 0x18, 0x20, 0xC1, 0xFC, 0x36, 0x66, 0x66, 0x6E,
0xCE, 0x66, 0x66, 0x66, 0x30, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0xC6, 0x66,
0x66, 0x67, 0x37, 0x66, 0x66, 0x66, 0xC0, 0x61, 0x24, 0x38 };
const GFXglyph FreeSans9pt7bGlyphs[] PROGMEM = {
{ 0, 0, 0, 5, 0, 1 }, // 0x20 ' '
{ 0, 2, 13, 6, 2, -12 }, // 0x21 '!'
{ 4, 5, 4, 6, 1, -12 }, // 0x22 '"'
{ 7, 10, 12, 10, 0, -11 }, // 0x23 '#'
{ 22, 9, 16, 10, 1, -13 }, // 0x24 '$'
{ 40, 16, 13, 16, 1, -12 }, // 0x25 '%'
{ 66, 11, 13, 12, 1, -12 }, // 0x26 '&'
{ 84, 2, 4, 4, 1, -12 }, // 0x27 '''
{ 85, 4, 17, 6, 1, -12 }, // 0x28 '('
{ 94, 4, 17, 6, 1, -12 }, // 0x29 ')'
{ 103, 5, 5, 7, 1, -12 }, // 0x2A '*'
{ 107, 6, 8, 11, 3, -7 }, // 0x2B '+'
{ 113, 2, 4, 5, 2, 0 }, // 0x2C ','
{ 114, 4, 1, 6, 1, -4 }, // 0x2D '-'
{ 115, 2, 1, 5, 1, 0 }, // 0x2E '.'
{ 116, 5, 13, 5, 0, -12 }, // 0x2F '/'
{ 125, 8, 13, 10, 1, -12 }, // 0x30 '0'
{ 138, 4, 13, 10, 3, -12 }, // 0x31 '1'
{ 145, 9, 13, 10, 1, -12 }, // 0x32 '2'
{ 160, 8, 13, 10, 1, -12 }, // 0x33 '3'
{ 173, 7, 13, 10, 2, -12 }, // 0x34 '4'
{ 185, 9, 13, 10, 1, -12 }, // 0x35 '5'
{ 200, 9, 13, 10, 1, -12 }, // 0x36 '6'
{ 215, 8, 13, 10, 0, -12 }, // 0x37 '7'
{ 228, 9, 13, 10, 1, -12 }, // 0x38 '8'
{ 243, 8, 13, 10, 1, -12 }, // 0x39 '9'
{ 256, 2, 10, 5, 1, -9 }, // 0x3A ':'
{ 259, 3, 12, 5, 1, -8 }, // 0x3B ';'
{ 264, 9, 9, 11, 1, -8 }, // 0x3C '<'
{ 275, 9, 4, 11, 1, -5 }, // 0x3D '='
{ 280, 9, 9, 11, 1, -8 }, // 0x3E '>'
{ 291, 9, 13, 10, 1, -12 }, // 0x3F '?'
{ 306, 17, 16, 18, 1, -12 }, // 0x40 '@'
{ 340, 12, 13, 12, 0, -12 }, // 0x41 'A'
{ 360, 11, 13, 12, 1, -12 }, // 0x42 'B'
{ 378, 11, 13, 13, 1, -12 }, // 0x43 'C'
{ 396, 11, 13, 13, 1, -12 }, // 0x44 'D'
{ 414, 9, 13, 11, 1, -12 }, // 0x45 'E'
{ 429, 8, 13, 11, 1, -12 }, // 0x46 'F'
{ 442, 12, 13, 14, 1, -12 }, // 0x47 'G'
{ 462, 11, 13, 13, 1, -12 }, // 0x48 'H'
{ 480, 2, 13, 5, 2, -12 }, // 0x49 'I'
{ 484, 7, 13, 10, 1, -12 }, // 0x4A 'J'
{ 496, 11, 13, 12, 1, -12 }, // 0x4B 'K'
{ 514, 8, 13, 10, 1, -12 }, // 0x4C 'L'
{ 527, 13, 13, 15, 1, -12 }, // 0x4D 'M'
{ 549, 11, 13, 13, 1, -12 }, // 0x4E 'N'
{ 567, 13, 13, 14, 1, -12 }, // 0x4F 'O'
{ 589, 10, 13, 12, 1, -12 }, // 0x50 'P'
{ 606, 13, 14, 14, 1, -12 }, // 0x51 'Q'
{ 629, 12, 13, 13, 1, -12 }, // 0x52 'R'
{ 649, 10, 13, 12, 1, -12 }, // 0x53 'S'
{ 666, 9, 13, 11, 1, -12 }, // 0x54 'T'
{ 681, 11, 13, 13, 1, -12 }, // 0x55 'U'
{ 699, 11, 13, 12, 0, -12 }, // 0x56 'V'
{ 717, 17, 13, 17, 0, -12 }, // 0x57 'W'
{ 745, 12, 13, 12, 0, -12 }, // 0x58 'X'
{ 765, 12, 13, 12, 0, -12 }, // 0x59 'Y'
{ 785, 10, 13, 11, 1, -12 }, // 0x5A 'Z'
{ 802, 3, 17, 5, 1, -12 }, // 0x5B '['
{ 809, 5, 13, 5, 0, -12 }, // 0x5C '\'
{ 818, 3, 17, 5, 0, -12 }, // 0x5D ']'
{ 825, 7, 7, 8, 1, -12 }, // 0x5E '^'
{ 832, 10, 1, 10, 0, 3 }, // 0x5F '_'
{ 834, 4, 3, 5, 0, -12 }, // 0x60 '`'
{ 836, 9, 10, 10, 1, -9 }, // 0x61 'a'
{ 848, 9, 13, 10, 1, -12 }, // 0x62 'b'
{ 863, 8, 10, 9, 1, -9 }, // 0x63 'c'
{ 873, 8, 13, 10, 1, -12 }, // 0x64 'd'
{ 886, 8, 10, 10, 1, -9 }, // 0x65 'e'
{ 896, 4, 13, 5, 1, -12 }, // 0x66 'f'
{ 903, 8, 14, 10, 1, -9 }, // 0x67 'g'
{ 917, 8, 13, 10, 1, -12 }, // 0x68 'h'
{ 930, 2, 13, 4, 1, -12 }, // 0x69 'i'
{ 934, 4, 17, 4, 0, -12 }, // 0x6A 'j'
{ 943, 9, 13, 9, 1, -12 }, // 0x6B 'k'
{ 958, 2, 13, 4, 1, -12 }, // 0x6C 'l'
{ 962, 13, 10, 15, 1, -9 }, // 0x6D 'm'
{ 979, 8, 10, 10, 1, -9 }, // 0x6E 'n'
{ 989, 8, 10, 10, 1, -9 }, // 0x6F 'o'
{ 999, 9, 13, 10, 1, -9 }, // 0x70 'p'
{ 1014, 8, 13, 10, 1, -9 }, // 0x71 'q'
{ 1027, 5, 10, 6, 1, -9 }, // 0x72 'r'
{ 1034, 8, 10, 9, 1, -9 }, // 0x73 's'
{ 1044, 4, 12, 5, 1, -11 }, // 0x74 't'
{ 1050, 8, 10, 10, 1, -9 }, // 0x75 'u'
{ 1060, 9, 10, 9, 0, -9 }, // 0x76 'v'
{ 1072, 13, 10, 13, 0, -9 }, // 0x77 'w'
{ 1089, 8, 10, 9, 0, -9 }, // 0x78 'x'
{ 1099, 9, 14, 9, 0, -9 }, // 0x79 'y'
{ 1115, 7, 10, 9, 1, -9 }, // 0x7A 'z'
{ 1124, 4, 17, 6, 1, -12 }, // 0x7B '{'
{ 1133, 2, 17, 4, 2, -12 }, // 0x7C '|'
{ 1138, 4, 17, 6, 1, -12 }, // 0x7D '}'
{ 1147, 7, 3, 9, 1, -7 } }; // 0x7E '~'
const GFXfont FreeSans9pt7b PROGMEM = {
(uint8_t *)FreeSans9pt7bBitmaps,
(GFXglyph *)FreeSans9pt7bGlyphs,
0x20, 0x7E, 22 };
// Approx. 1822 bytes

123
libraries/fonts/Picopixel.h Executable file
View File

@ -0,0 +1,123 @@
// Picopixel by Sebastian Weber. A tiny font
// with all characters within a 6 pixel height.
const uint8_t PicopixelBitmaps[] PROGMEM = {
0xE8, 0xB4, 0x57, 0xD5, 0xF5, 0x00, 0x4E, 0x3E, 0x80, 0xA5, 0x4A, 0x4A,
0x5A, 0x50, 0xC0, 0x6A, 0x40, 0x95, 0x80, 0xAA, 0x80, 0x5D, 0x00, 0x60,
0xE0, 0x80, 0x25, 0x48, 0x56, 0xD4, 0x75, 0x40, 0xC5, 0x4E, 0xC5, 0x1C,
0x97, 0x92, 0xF3, 0x1C, 0x53, 0x54, 0xE5, 0x48, 0x55, 0x54, 0x55, 0x94,
0xA0, 0x46, 0x64, 0xE3, 0x80, 0x98, 0xC5, 0x04, 0x56, 0xC6, 0x57, 0xDA,
0xD7, 0x5C, 0x72, 0x46, 0xD6, 0xDC, 0xF3, 0xCE, 0xF3, 0x48, 0x72, 0xD4,
0xB7, 0xDA, 0xF8, 0x24, 0xD4, 0xBB, 0x5A, 0x92, 0x4E, 0x8E, 0xEB, 0x58,
0x80, 0x9D, 0xB9, 0x90, 0x56, 0xD4, 0xD7, 0x48, 0x56, 0xD4, 0x40, 0xD7,
0x5A, 0x71, 0x1C, 0xE9, 0x24, 0xB6, 0xD4, 0xB6, 0xA4, 0x8C, 0x6B, 0x55,
0x00, 0xB5, 0x5A, 0xB5, 0x24, 0xE5, 0x4E, 0xEA, 0xC0, 0x91, 0x12, 0xD5,
0xC0, 0x54, 0xF0, 0x90, 0xC7, 0xF0, 0x93, 0x5E, 0x71, 0x80, 0x25, 0xDE,
0x5E, 0x30, 0x6E, 0x80, 0x77, 0x9C, 0x93, 0x5A, 0xB8, 0x45, 0x60, 0x92,
0xEA, 0xAA, 0x40, 0xD5, 0x6A, 0xD6, 0x80, 0x55, 0x00, 0xD7, 0x40, 0x75,
0x90, 0xE8, 0x71, 0xE0, 0xBA, 0x40, 0xB5, 0x80, 0xB5, 0x00, 0x8D, 0x54,
0xAA, 0x80, 0xAC, 0xE0, 0xE5, 0x70, 0x6A, 0x26, 0xFC, 0xC8, 0xAC, 0x5A };
const GFXglyph PicopixelGlyphs[] PROGMEM = {
{ 0, 0, 0, 2, 0, 1 }, // 0x20 ' '
{ 0, 1, 5, 2, 0, -4 }, // 0x21 '!'
{ 1, 3, 2, 4, 0, -4 }, // 0x22 '"'
{ 2, 5, 5, 6, 0, -4 }, // 0x23 '#'
{ 6, 3, 6, 4, 0, -4 }, // 0x24 '$'
{ 9, 3, 5, 4, 0, -4 }, // 0x25 '%'
{ 11, 4, 5, 5, 0, -4 }, // 0x26 '&'
{ 14, 1, 2, 2, 0, -4 }, // 0x27 '''
{ 15, 2, 5, 3, 0, -4 }, // 0x28 '('
{ 17, 2, 5, 3, 0, -4 }, // 0x29 ')'
{ 19, 3, 3, 4, 0, -3 }, // 0x2A '*'
{ 21, 3, 3, 4, 0, -3 }, // 0x2B '+'
{ 23, 2, 2, 3, 0, 0 }, // 0x2C ','
{ 24, 3, 1, 4, 0, -2 }, // 0x2D '-'
{ 25, 1, 1, 2, 0, 0 }, // 0x2E '.'
{ 26, 3, 5, 4, 0, -4 }, // 0x2F '/'
{ 28, 3, 5, 4, 0, -4 }, // 0x30 '0'
{ 30, 2, 5, 3, 0, -4 }, // 0x31 '1'
{ 32, 3, 5, 4, 0, -4 }, // 0x32 '2'
{ 34, 3, 5, 4, 0, -4 }, // 0x33 '3'
{ 36, 3, 5, 4, 0, -4 }, // 0x34 '4'
{ 38, 3, 5, 4, 0, -4 }, // 0x35 '5'
{ 40, 3, 5, 4, 0, -4 }, // 0x36 '6'
{ 42, 3, 5, 4, 0, -4 }, // 0x37 '7'
{ 44, 3, 5, 4, 0, -4 }, // 0x38 '8'
{ 46, 3, 5, 4, 0, -4 }, // 0x39 '9'
{ 48, 1, 3, 2, 0, -3 }, // 0x3A ':'
{ 49, 2, 4, 3, 0, -3 }, // 0x3B ';'
{ 50, 2, 3, 3, 0, -3 }, // 0x3C '<'
{ 51, 3, 3, 4, 0, -3 }, // 0x3D '='
{ 53, 2, 3, 3, 0, -3 }, // 0x3E '>'
{ 54, 3, 5, 4, 0, -4 }, // 0x3F '?'
{ 56, 3, 5, 4, 0, -4 }, // 0x40 '@'
{ 58, 3, 5, 4, 0, -4 }, // 0x41 'A'
{ 60, 3, 5, 4, 0, -4 }, // 0x42 'B'
{ 62, 3, 5, 4, 0, -4 }, // 0x43 'C'
{ 64, 3, 5, 4, 0, -4 }, // 0x44 'D'
{ 66, 3, 5, 4, 0, -4 }, // 0x45 'E'
{ 68, 3, 5, 4, 0, -4 }, // 0x46 'F'
{ 70, 3, 5, 4, 0, -4 }, // 0x47 'G'
{ 72, 3, 5, 4, 0, -4 }, // 0x48 'H'
{ 74, 1, 5, 2, 0, -4 }, // 0x49 'I'
{ 75, 3, 5, 4, 0, -4 }, // 0x4A 'J'
{ 77, 3, 5, 4, 0, -4 }, // 0x4B 'K'
{ 79, 3, 5, 4, 0, -4 }, // 0x4C 'L'
{ 81, 5, 5, 6, 0, -4 }, // 0x4D 'M'
{ 85, 4, 5, 5, 0, -4 }, // 0x4E 'N'
{ 88, 3, 5, 4, 0, -4 }, // 0x4F 'O'
{ 90, 3, 5, 4, 0, -4 }, // 0x50 'P'
{ 92, 3, 6, 4, 0, -4 }, // 0x51 'Q'
{ 95, 3, 5, 4, 0, -4 }, // 0x52 'R'
{ 97, 3, 5, 4, 0, -4 }, // 0x53 'S'
{ 99, 3, 5, 4, 0, -4 }, // 0x54 'T'
{ 101, 3, 5, 4, 0, -4 }, // 0x55 'U'
{ 103, 3, 5, 4, 0, -4 }, // 0x56 'V'
{ 105, 5, 5, 6, 0, -4 }, // 0x57 'W'
{ 109, 3, 5, 4, 0, -4 }, // 0x58 'X'
{ 111, 3, 5, 4, 0, -4 }, // 0x59 'Y'
{ 113, 3, 5, 4, 0, -4 }, // 0x5A 'Z'
{ 115, 2, 5, 3, 0, -4 }, // 0x5B '['
{ 117, 3, 5, 4, 0, -4 }, // 0x5C '\'
{ 119, 2, 5, 3, 0, -4 }, // 0x5D ']'
{ 121, 3, 2, 4, 0, -4 }, // 0x5E '^'
{ 122, 4, 1, 4, 0, 1 }, // 0x5F '_'
{ 123, 2, 2, 3, 0, -4 }, // 0x60 '`'
{ 124, 3, 4, 4, 0, -3 }, // 0x61 'a'
{ 126, 3, 5, 4, 0, -4 }, // 0x62 'b'
{ 128, 3, 3, 4, 0, -2 }, // 0x63 'c'
{ 130, 3, 5, 4, 0, -4 }, // 0x64 'd'
{ 132, 3, 4, 4, 0, -3 }, // 0x65 'e'
{ 134, 2, 5, 3, 0, -4 }, // 0x66 'f'
{ 136, 3, 5, 4, 0, -3 }, // 0x67 'g'
{ 138, 3, 5, 4, 0, -4 }, // 0x68 'h'
{ 140, 1, 5, 2, 0, -4 }, // 0x69 'i'
{ 141, 2, 6, 3, 0, -4 }, // 0x6A 'j'
{ 143, 3, 5, 4, 0, -4 }, // 0x6B 'k'
{ 145, 2, 5, 3, 0, -4 }, // 0x6C 'l'
{ 147, 5, 3, 6, 0, -2 }, // 0x6D 'm'
{ 149, 3, 3, 4, 0, -2 }, // 0x6E 'n'
{ 151, 3, 3, 4, 0, -2 }, // 0x6F 'o'
{ 153, 3, 4, 4, 0, -2 }, // 0x70 'p'
{ 155, 3, 4, 4, 0, -2 }, // 0x71 'q'
{ 157, 2, 3, 3, 0, -2 }, // 0x72 'r'
{ 158, 3, 4, 4, 0, -3 }, // 0x73 's'
{ 160, 2, 5, 3, 0, -4 }, // 0x74 't'
{ 162, 3, 3, 4, 0, -2 }, // 0x75 'u'
{ 164, 3, 3, 4, 0, -2 }, // 0x76 'v'
{ 166, 5, 3, 6, 0, -2 }, // 0x77 'w'
{ 168, 3, 3, 4, 0, -2 }, // 0x78 'x'
{ 170, 3, 4, 4, 0, -2 }, // 0x79 'y'
{ 172, 3, 4, 4, 0, -3 }, // 0x7A 'z'
{ 174, 3, 5, 4, 0, -4 }, // 0x7B '{'
{ 176, 1, 6, 2, 0, -4 }, // 0x7C '|'
{ 177, 3, 5, 4, 0, -4 }, // 0x7D '}'
{ 179, 4, 2, 5, 0, -3 } }; // 0x7E '~'
const GFXfont Picopixel PROGMEM = {
(uint8_t *)PicopixelBitmaps,
(GFXglyph *)PicopixelGlyphs,
0x20, 0x7E, 7 };
// Approx. 852 bytes

BIN
tools/ESP32FS/tool/esp32fs.jar Executable file

Binary file not shown.