ANNEX32 WI-FI RDS
User Manual
Version beta
1.60.3
© ciccioCB
2024
COPYRIGHT
The Annex firmware, including the AnnexToolKit
and this manual, are Copyright 2017-2024 by Francesco Ceccarella
(ciccioCB).
The compiled object code (the .bin file) for
the Annex firmware is free software: you can use or redistribute it
as you please except for commercial purposes. It is not allowed to distribute or embed it into
products that are sold or for any other activity making or intended
to make a profit.
The compiled object code (the .exe file) for
the AnnexToolKit utility is free software: you can use or
redistribute it as you please except for commercial purposes.
It is not allowed to distribute or
embed it into products that are sold or for any other activity
making or intended to make a profit.
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.
This manual is distributed under a Creative
Commons Attribution-NonCommercial-ShareAlike 3.0 France license
(CC BY-NC-SA 3.0)
The above copyright notice and this permission
notice shall be included in all copies or redistributions of the
Software in any form.
License and
credits
The base of the interpreter
comes from the original project "MiniBasic" by Malcom
Mclean.
Adafruit BNO055 Orientation
Sensor library is written by KTOWN is Copyright © Adafruit
Industries. It is released under MIT license.
TFT_eSPI display Library is
Copyright © 2017 Bodmer. It is released under FreeBSD
license.
Adafruit PWM Servo Driver
Library is Copyright © Adafruit. It is released under MIT
license.
Arduino Library for Dallas
Temperature ICs is Copyright © Miles Burton
<miles@mnetcs.com>. It is released under LGPL
license.
OneWire Library is Copyright
1999-2006 Dallas Semiconductor Corporation and Copyright © 2007,
Jim Studt.
Adafruit DHT Humidity &
Temperature Sensor Library is Copyright © Adafruit. It is released
under MIT license.
ESP8266 and ESP32 Oled Driver
for SSD1306 display is Copyright © 2016 by Daniel Eichhorn and
Copyright © 2016 by Fabrice Weinberg
NeoPixelBus library is
Copyright © Michael C. Miller. It is released under LGPL
license.
ESP AsyncTCP library is
Copyright © 2016 Hristo Gochkov. It is released under LGPL
license.
ESP AsyncWebServer library is
Copyright © 2016 Hristo Gochkov. It is released under LGPL
license.
IRremote library is Copyright ©
Sebastien Warin, Mark Szabo, Ken Shirriff, David Conran. It is
released under LGPL license.
uRTCLib is is Copyright © 2015
Naguissa (naguissa.com@gmail.com). It is released under LGPL
license.
BME280 library is written by
Limor Fried/Ladyada for Adafruit Industries. It is released under
BSD license,
APDS9960 library is written by
Shawn Hymel for Sparkfun Electronics. It is released under Beerware
license.
PID Library is written by Brett
Beauregard (br3ttb@gmail.com). It is released under MIT
license.
The Javascript Editor
EditArea is Copyright © 2008 Christophe Dolivet. It is
released under BSD license.
The M5Stack library is
copyright © 2017 by M5Stack. It is released under MIT
license.
The MPU9250 driver is part of
the M5Stack Library.
The VL53L0X driver is Copyright
© 2017 Pololu. It contains code © 2016
STMicroelectronics.
Some GUI objects come from the
library GUIslice Copyright © Calvin Hass that is released under MIT
license.
The MFRD522 library is written
by Miguel Balboa and is released as free and unencumbered software
released into the public domain.
Contributions
A very big thank you to Robin Baker
(Electroguard) for his great involvement in the project by
supporting all the tests on the real hardware (bought with his
money), and all the advices that allowed me to add a lot of
functionality, not to mention the Huge work he did while
documenting the project on the website.
Content :
Introduction: 31
Interpreter: 34
Branch labels.
34
Variables: 35
Arrays: 36
OPTION.BASE 1. 36
LBOUND(array() [, dimension]) :
Returns the lower bound of the specified array dimension.
37
UBOUND(array() [, dimension]) :
Returns the upper bound of the specified array dimension.
37
Scope of the variables: 38
Bases of the language.
39
OPERATORS AND PRECEDENCE..
39
Basic internal keywords: 41
IF command : 41
FOR loop.
42
WHILE WEND loop.
43
DO LOOP loop.
44
SELECT CASE..
45
GOTO..
46
GOSUB..
46
DATA..
47
END..
48
EXIT.
48
SUB..
48
Logical / boolean Operations.
50
ERRORS HANDLING..
51
ONERROR ABORT.
52
ONERROR IGNORE.
52
ONERROR SKIP [nn].
52
ONERROR CLEAR.
52
ONERROR GOTO [label |
OFF].
52
BAS.ERRLINE.
52
BAS.ERRNUM.
52
BAS.ERRMSG$.
52
HOW the interpreter works with the HTML
code and Objects : 53
HTML Objects.
57
TIMERS..
62
EVENTS..
62
Button Event 63
OnHtmlChange Event 63
OnHtmlReloadEvent 63
OnInfrared Event 64
OnSerial Event 64
OnSerial2 Event 64
OnTouch Event 65
OnUDP Event 65
OnWgetAsync Event 65
OnUrlMessage Event 66
OnEspNowMsg Event 69
OnEspNowError Event 70
OnMQTT Event 70
OnPlay Event 70
WiFI CONNECTIONS..
70
PROGRAM AUTORUN..
72
RECOVERY MODE..
73
SLEEP mode (low energy) and RTC
memory.
73
DATE - TIME timekeeper 74
Unix Time functions.
75
FAT32 File System..
75
FILE.COPY(filename$,
newfile$).
77
FILE.DELETE(filename$).
77
FILE.EXISTS(filename$).
77
FILE.RENAME(oldname$,
newname$).
77
FILE.SIZE(filename$).
77
FILE.MKDIR(dirname$).
77
FILE.RMDIR(dirname$).
77
FILE.DIR$(path$).
77
FILE.READ$(filename$, [line_num] | [start,
length]).
77
FILE.APPEND filename$,
content$.
77
FILE.SAVE filename$,
content$.
78
FILE.WRITE filename$,
content$.
78
FILE.FROMBASE64 source$,
dest$.
78
FILE.TOBASE64 source$,
dest$.
78
FILE.SAVE_IOBUFF.
78
FILE.WRITE_IOBUFF.
78
FILE.APPEND_IOBUFF.
78
FILE.READ_IOBUFF.
78
Download files from another module or WEB
server 79
FILE.DOWNLOAD url$,
file_path$.
79
Notes: 79
I/O BUFFERS..
80
Read Operations.
82
Write Operations.
82
Special operations.
83
Advanced operations.
83
Bit operations.
84
Buffer copy.
84
Code examples : 84
WIRING..
87
DIGITAL I/O..
88
PIN.STRENGTH 15, 2.
89
PIN SERIAL SHIFTING..
89
PIN.SHIFTOUT pin_data, pin_clk, data [,
bit_order] [, nb_bits] [, delay_us].
90
PIN.SHIFTIN( pin_data, pin_clk [, bit_order]
[, nb_bits] [, delay_us] ).
91
PIN INTERRUPTS..
91
Analog inputs.
93
TOUCH inputs.
93
Analog outputs.
93
Hardware interfaces: 94
PWM..
94
PWM.SETUP pin, channel, default_value,
[,frequency] [,resolution].
95
PWM.OUT channel,
value.
95
SERVO..
95
I2S BUS..
97
SPEAKER OUTPUT.
99
I2C BUS..
100
PCF8574 Module.
101
ADS1115 Module.
102
MCP23017 Module.
105
SPI BUS..
106
74HC595 Module.
109
MCP23S17 Module.
110
CAN BUS..
112
CAN.SETUP..
113
CAN.INIT.
114
CAN.STOP..
114
CAN.WRITE..
114
CAN.WRITE_IOBUFF.
115
ONCANBUS..
115
CANBUS BUFFERS..
117
RMT Module.
119
Key Features.
119
Memory Management and Synchronisation
of RMT Channels.
120
Clock Divider.
120
RMT RAM Composition.
121
Memory Block Extension.
122
RMT Transmit
Synchronisation.
123
TX Transmitter Mode.
123
RX Reception Mode.
124
RX Reception Event 124
RMT Command Functions.
124
RMT.SETUP_TX channel, pin [, clk_div] [,
mem_block_num] [, idle_level] [, loop_en] [, carrier_en] [,
carrier_freq_hz] [, carrier_duty_percent] [,
carrier_level].
124
RMT.WRITE channel, num_items, name [,
wait].
125
RMT.ENCODE channel, num_items, array() [,
wait].
125
RMT.SETUP_RX channel, pin [, clk_div] [,
mem_block_num] [, invert] [, filter_en] [, filter_ticks_thresh] [,
idle_threshold] [, rm_carrier] [, carrier_freq_hz] [,
carrier_duty_percent] [, carrier_level].
125
RMT.READ array().
126
RMT.DECODE array().
126
RMT.ADD_GROUP channel.
126
RMT.DEL_GROUP channel.
126
RMT.END channel.
127
Synchronisation in
Groups.
127
Example 1: RMT Transmission of a Pulse
Sequence.
127
Example 2: RMT Transmission of a
Sinusoidal Pattern.
129
Example 3: Synchronized RMT
Transmission of Sinusoidal Patterns.
131
Example 4: Infrared Signal Reception
and Decoding with RMT on ESP32.
133
COUNTERS..
135
PID controllers.
136
SOUND PLAYER..
138
Metadata Decoding from Mp3 and
streaming.
140
SPEECH SYNTHESIS with vintage C64 SAM
speaker 141
SPEECH SYNTHESIS using google
translate.
142
SPEECH SYNTHESIS using voiceRSS free
service.
143
VS1053B Audio Decoder 144
VS1053.SETUP XCS_pin, XDCS_pin, DREQ_pin
[,info_enabled] [,SPIfreq] [SCI_CLOCKF].
146
VS1053.PLAY file$.
146
VS1053.STREAM
streaming_url$.
147
VS1053.VOICE "message",
"language".
147
VS1053.STOP.
147
VS1053.VOLUME vol.
147
VS1053.RESET.
147
VS1053.INIT patchFile$.
148
VS1053.INIT
"/patches/vs1053b-patches-flac.cmd".
148
VS1053.WRITE register,
value.
148
VS1053.READ register.
148
VS1053.MIDI_CMD cmd, data1,
data2.
148
VS1053.NOTE_ON channel, note,
velocity.
148
VS1053.NOTE_OFF channel, note,
velocity.
148
LCD DISPLAY USING I2C..
148
OLED DISPLAY..
153
ST7920 LCD DISPLAY..
155
RTC module.
157
PCA9685 (PWM / Servo) Module.
159
TM1637 display module.
160
TM1638 display module.
162
MAX7219 8-Digits 7-segment
display.
164
MAX7219 Dot Matrix Display.
165
NeoPixel WS2812B led strips.
167
NEO.SETUP pin,
[nb_led].
169
NEO.STRIP led_start_pos, led_end_pos, R, G,
B [, disable].
169
NEO.STRIP led_start_pos, led_end_pos, COLOR
[, disable].
169
NEO.PIXEL led_pos, R, G, B [,
disable].
169
NEO.PIXEL led_pos, COLOR [,
disable].
169
NEO.RGB(R, G, B).
169
NEO.GETPIXEL(led_pos).
169
NEO.ROTATELEFT num_steps, [led_end_pos,
led_end_pos, disable].
169
NEO.ROTATERIGHT num_steps, [led_end_pos,
led_end_pos, disable].
169
NEO.SHIFTLEFT num_steps, [led_end_pos,
led_end_pos, disable].
169
NEO.SHIFTRIGHT num_steps, [led_end_pos,
led_end_pos, disable].
170
NEO.REFRESH.
170
NEO.DIM(COLOR , Gain).
170
NEO.LIGHTEN(COLOR ,
Gain).
170
NEO.DARKEN(COLOR ,
Gain).
170
NEO.LINEARBLEND(COLOR1, COLOR2,
progress).
170
NEO.BILINEARBLEND(.
170
Upper_Left_COLOR, Upper_Right_COLOR,
Lower_Left_COLOR, Lower_Right_COLOR, x, y).
170
NeoPixel based WS2812b Dot Matrix
DIsplay.
171
NEOSCROLL.SETUP nb_devices, nb_lines, pin
[,layout] [,width, height, orientation].
175
NEOSCROLL.DELETE.
175
NEOSCROLL.FILL color, [x, y, width,
height].
175
NEOSCROLL.TEXT.POS x,
y.
175
NEOSCROLL.TEXT.FONT
font.
175
NEOSCROLL.SHOW x, y.
175
NEOSCROLL.TEXT.BRIGHTNESS
brightness.
175
NEOSCROLL.BRIGHTNESS
brightness.
175
NEOSCROLL.PRINT text$,
color$.
176
NEOSCROLL.SPRITESHEET
image$.
176
NEOSCROLL.SPRITE x, y, width, height,
x_in_bmp, y_in_bmp.
177
Copy a portion of the SPRITESHEET image into the canvas
using the parameters given.
177
NEOSCROLL.LIMITS [x1,] [x2], [y1],
[y2].
177
NEOSCROLL.SYNC.
177
NEOSCROLL.MODE mode.
177
NEOSCROLL.SCROLL.
177
NEOSCROLL.SCROLL.
177
Scroll the image using the current
MODE and within
the current LIMITS.c.
177
NEOSCROLL.OSCILLATE.
177
NEOSCROLL.OSCILLATE.
177
Oscillate the image using the current
MODE and within
the current LIMITS.c.
177
NEOSCROLL.X.
177
NEOSCROLL.Y.
177
HUB75 Matrix Displays -
DMAMATRIX..
181
DMAMATRIX.INIT R1, G1, B1, R2, G2, B2, A, B,
C, D, E, LAT, OE, CLK [,freq_DMA]
[,resolution].
182
DMAMATRIX.SETUP nb_devices, nb_lines
[,layout] [,width, height, orientation].
182
DMAMATRIX.DELETE.
182
DMAMATRIX.FILL color, [x, y, width,
height].
182
DMAMATRIX.TEXT.POS x,
y.
182
DMAMATRIX.TEXT.FONT
font.
183
DMAMATRIX.TEXT.COLOR
color.
183
DMAMATRIX.SHOW [x, y].
183
DMAMATRIX.TEXT.BRIGHTNESS
brightness.
183
DMAMATRIX.BRIGHTNESS
brightness.
183
DMAMATRIX.PRINT text$ [, color$ |
color].
184
DMAMATRIX.SPRITESHEET
image$.
184
DMAMATRIX.SPRITE x, y, width, height,
x_in_bmp, y_in_bmp.
185
Copy a portion of the SPRITESHEET image into the canvas
using the parameters given.
185
DMAMATRIX.LIMITS [x1,] [x2], [y1],
[y2].
185
DMAMATRIX.SYNC.
185
DMAMATRIX.MODE mode.
185
DMAMATRIX.SCROLL.
185
DMAMATRIX.SCROLL.
185
Scroll the image using the current
MODE and within
the current LIMITS.c.
185
DMAMATRIX.OSCILLATE.
185
DMAMATRIX.OSCILLATE.
185
Oscillate the image using the current
MODE and within
the current LIMITS.c.
185
DMAMATRIX.PIXEL x, y,
color.
185
DMAMATRIX.LINE x1, y1, x2, y1,
color.
185
DMAMATRIX.CIRCLE x, y, radius, color
[,fill].
185
DMAMATRIX.RECT x, y, w, h, color
[,fill].
185
DMAMATRIX.X.
185
DMAMATRIX.Y.
186
DMAMATRIX.POSX.
186
DMAMATRIX.POSY.
186
DMAMATRIX.PLAYGIF gif$ [,x ,
y].
186
DMAMATRIX.LOADGIF gif$ [,x ,
y].
186
DMAMATRIX.FRAMEGIF [do_not_show
[,loop]].
186
SD CARD ADAPTER..
186
TFT DISPLAY ILI9341.
188
TFT DISPLAY ILI9163.
192
TFT DISPLAY ILI9486.
193
TFT DISPLAY ILI9481.
196
TFT DISPLAY ILI9488.
197
TFT DISPLAY ST7735.
198
TFT DISPLAY ST7796.
199
TFT DISPLAY ST7789.
202
OLED DISPLAY SSD1351 RGB..
203
TFT DISPLAY GC9A01.
204
TouchScreen - Resistive.
204
TouchScreen - Capacitive.
205
TFT FONTS..
206
QR CODES..
210
GRAPHIC GUI for TFT.
210
GUI Objects.
211
gui.TextLine.
211
gui.Button.
211
gui.Image.
212
gui.ButtonImage.
212
gui.CheckBox.
213
gui.Slider.
214
gui.ProgressBar.
214
gui.Ramp.
214
gui.Gauge.
215
gui.Box.
215
gui.Circle.
216
gui.Rect 216
gui.Line.
217
GUI Functions.
217
gui.GetValue.
217
gui.Target 217
GUI Commands.
217
gui.INIT..
217
gui.REDRAW...
218
gui.REFRESH..
218
gui.AUTOREFRESH..
218
gui.SETVALUE..
218
gui.SETTEXT..
218
gui.SETIMAGE..
218
gui.SETCOLOR..
218
gui.SETRANGE..
219
gui.SETEVENT..
219
gui.SETSTYLE..
220
LCD RGB DISPLAY INTERFACE (module
ESP32-8048S070C) 221
VGA.PINOUT R0, R1, R2, R3, R4, G0, G1, G2,
G3, G4, G5, B0, B1, B2, B3, B4, HSYNC, VSYNC, DE,
pCLK.
221
VGA DISPLAY INTERFACE..
221
VGA.PINOUT R0, R1, R2, G0, G1, G2, B0, B1,
HSYNC, VSYNC.
224
VGA.PINOUT R0, R1, R2, R3, R4, G0, G1, G2,
G3, G4, G5, B0, B1, B2, B3, B4, HSYNC, VSYNC, DE,
pCLK.
224
VGA.SETUP hFront,hSync, hBack, hRes, vFront,
vSync, vBack, vRes, frequency [,vClones=1] [,nb_pages=1]
[,outSize=1] [,aligned].
224
VGA.INIT mode
[,nb_pages=1].
224
VGA.DELETE.
224
VGA.STOP.
224
VGA.START.
224
VGA.SHOW.
224
VGA.SHOWPAGE page.
225
VGA.WRITEPAGE page.
225
VGA.SAVE file$.
225
VGA.FILL color.
225
VGA.COPY src, dest [x, y, w, h [dest_x,
dest_y].
225
VGA.PIXEL x, y, color.
225
VGA.LINE x1, y1, x2, y2, color
[,thickness=1].
225
VGA.CIRCLE x, y, radius, color
[,fill].
225
VGA.RECT x, y, w, h, color
[,fill].
225
VGA.TRIANGLE x1, y1, x2, y2, x3, y3, color
[,fill=1].
225
VGA.NEEDLE x, y, length, angle, color
[,thickness=1].
225
VGA.NEXT x, y, length, angle, color
[,thickness=1].
225
VGA.TEXT.POS x, y.
225
VGA.TEXT.FONT font.
226
VGA.TEXT.COLOR color
[,background].
226
VGA.TEXT.SIZE size.
226
VGA.PRINT var
[var$].
226
VGA.TEXT.ALIGN align.
226
VGA.TEXT.PADDING width.
226
VGA.TEXT.DRAW "text", x, y
[,font].
226
VGA.IMAGE file$ [, x,
y].
226
VGA.SPRITESHEET
image$.
227
VGA.SPRITE x, y, width, height, x_in_bmp,
y_in_bmp.
227
VGA.SETSPRITE id, width, height, x_in_bmp,
y_in_bmp [, nextframe_delta_x,
nextframe_delta_y].
227
VGA.DRAWSPRITE id, x, y,
[frame=0].
227
VGA.REMOVESPRITE id, src,
dest.
227
VGA.HIDESPRITE id,
visibility.
227
VGA GUI (experimental) 227
VGAGUI.SETSPRITE x_in_bmp, y_in_bmp, width, height,
nextframe_delta_x, nextframe_delta_y.
229
VGAGUI.SPRITE(x, y, width, height, frame_on,
frame_off, [,toggle=0] [,group]).
229
VGAGUI.ARC(x, y, width, height, value
[,thickness=10]).
230
VGAGUI.TEXTAREA(x, y, width, height, "text",
[,font] [,alignment] [,color_text] [,color_back] [,color_frame]
[,margin] ).
231
VGAGUI.IGNORE obj.
231
VGAGUI.GETTEXT obj,
var$.
232
VGAGUI.SETVALUE obj,
value.
232
VGAGUI.SETTEXT obj,
text$.
232
VGAGUI.SETCOLOR object, col1 [,col2 [,col3
[,col4]]].
232
VGAGUI.SETSTYLE object, prop1 [,prop2
[,prop3 [,prop4]]].
233
USB HID INTERFACE (Mouse, Keyboard,
Gamepad) - ESP32-S3 only.
234
How use the USB devices.
235
INFRARED INTERFACE..
237
ULTRASONIC DISTANCE SENSOR
HC-SR04.
241
DHT xx Temperature / Humidity
Sensors.
242
DS18B20 Temperature Sensors.
243
TEMPR$(pin_number, [ID],
[resolution]).
244
BNO055 Absolute Orientation Sensor
245
BME280 Combined humidity and pressure
sensor 247
BME680 Combined gas, pressure,
temperature & humidity sensor 249
BME680.TEMP.
251
BME680.PRESS.
251
BME680.HUM.
251
BME680.GASRES.
251
BME680.STATUS.
251
BME680.AVAIL.
251
BME680.SETSLEEPMODE.
251
BME680.SETFORCEDMODE.
251
BME680.SETOVERSAMPLING(ovs_temp, ovs_press,
ovs_hum).
251
BME680.SETIIRFILTER(filter).
252
BME680.SETGASON(temperature,
time).
252
BME680.SETGASOFF.
252
HDC1080 High Accuracy Digital Humidity
Sensor with Temperature Sensor 253
CCS811 Air Quality Sensor 254
APDS9960 Digital Proximity, Ambient
Light, RGB and Gesture Sensor 257
RFID MFRC522 RFID cards reader
260
Writing NUID for UID changeable card (4 byte UID version)
264
VL53L0X TOF (Time Of Flight) Distance
Sensor 264
HX711 - Weight Measurement
Module.
266
SI5351 Clock Generator Module.
268
SI5351.INIT [capacitor
[,crystal]].
270
SI5351.CALIB
correction.
270
SI5351.SETFREQ out_nb,
frequency.
270
SI5351.SETFREQ_MAN out_nb, frequency,
pll.
270
SI5351.STRENGTH out_nb,
strength.
270
SI5351.PHASE out_nb,
phase.
270
SI5351.RESET_PLL
pll_nb.
270
SI5351.ENABLE out_nb,
enable.
270
SI5351.INVERT out_nb,
invert.
270
SI5351.LOAD filename$.
271
STEP MOTOR..
271
STEPPER.SETUP stepper_id, pin_step,
pin_dir.
273
STEPPER.SETPARAM stepper_id, speed,
acceleration.
273
STEPPER.SETPOSITION stepper_id,
position.
273
STEPPER.MOVE stepper_id,
position.
273
STEPPER.MOVETO stepper_id,
position.
273
STEPPER.STOP
stepper_id.
273
STEPPER.FORCESTOP
stepper_id.
273
STEPPER.RUNFWD
stepper_id.
273
STEPPER.RUNBKD
stepper_id.
273
STEPPER.GETPOSITION(stepper_id).
274
STEPPER.GETTARGET(stepper_id).
274
MPU9250.
274
MPU6500 / MPU6050.
276
MPU6886 (For M5 Atom) 278
IMU FUSION FUNCTIONS..
280
ETHERNET Module W5500.
282
FTP..
286
BAS.FTP$.
286
Server data requests (GET, POST and
PUT) 287
-
WGET$(server$, port, [,header]
[,content_type$]).
288
-
WGET$(url$ [,header]
[,content_type$]).
288
-
WPOST$(server$, body$, port
[,header] [,content_type$] ).
288
-
WPOST$(url$, body$ [,header]
[,content_type$]).
288
-
WPUT$(server$, body$, port
[,header] [,content_type$] ).
288
-
WPUT$(url$, body$ [,header]
[,content_type$]).
288
-
WGETASYNC[(] server$, port,
[,header] [)].
288
-
WGETASYNC[(] url$,[,header]
[)].
288
MQTT.
290
Ret
= MQTT.Setup(server$ [,debug]).
292
Ret
= MQTT.Certif(cert_pem$ [,client_cert_pem$]
[,client_key_pem$]).
292
Ret
= MQTT.PSK(psk_hint_key$).
292
Ret
= MQTT.LWT(topic$, message$ [,Qos]
[,retain]).
292
Ret
= MQTT.Connect(login$, pass$ [,id$]).
292
Ret
= MQTT.Connect("", "" [,id$]).
292
Ret
= MQTT.Disconnect[()].
292
Ret
= MQTT.Publish(topic$, message$ [,Qos]
[,retain]).
292
Ret
= MQTT.Subscribe(topic$ [,Qos]).
292
Ret
= MQTT.UnSubscribe(topic$).
292
Ret
= MQTT.Connected[()].
292
Ret
= MQTT.Status[()].
293
ESP-NOW...
295
EspNow.Begin.
296
EspNow.Stop.
296
EspNow.Add_Peer(MAC_add$ [,interface]
[,channel]).
296
EspNow.Del_Peer.
296
EspNow.Write(msg$).
296
EspNow.Write(msg$,
MAC_add$).
296
EspNow.READ$.
297
ESPNow.REMOTE$.
297
ESPNow.ERROR$.
297
OnEspNowMsg label.
297
OnEspNowError label.
297
BLUETOOTH Low Energy (BLE) 306
Overview..
306
Operating Modes.
306
UUIDs and Characteristics.
307
Communication Details.
307
BLE Commands / Functions.
307
Event Handling.
308
TELEGRAM (messenger) support 311
LORA..
314
LoRa.Setup ss, reset,
dio0.
316
LoRa.Begin(freq).
316
LoRa.End.
316
LoRa.BeginPacket.
316
LoRa.Print.
316
LoRa.EndPacket.
316
LoRa.Receive.
316
LoRa.RSSI.
316
LoRa.SNR.
316
LoRa.Idle.
316
LoRa.Sleep.
316
LoRa.TXpower pow.
316
LoRa.SyncWord word.
317
LoRa.EnableCRC enable.
317
OnLora.
317
LoRa.Message$.
317
Modbus.
319
MODBUS.CONNECT IP$, [port] [,timeout]
[,idleTimeout].
319
MODBUS.DISCONNECT.
320
MODBUS.REQUEST token, serverID, functionCode [,p1] [,p2]
[,p3].
320
Modbus RTU Support 323
MODBUS.SetupRTU RX_pin, TX_pin, RE_DE_pin,
["BBBB,P,D,S"].
323
MODBUS.requestRTU ….. (see the
details in the chapter above) 323
MODBUS RTU wiring.
325
Regular Expressions (RegEx) 326
Patterns.
327
Magic characters.
327
Repetition.
328
Anchor to start and/or end of
string.
329
Captures.
329
Frontier patterns.
330
Multiple matches.
330
M5 Tough.
331
M5Tough.BatLevel.
333
M5Tough.BatVoltage.
333
M5Tough.BatCurrent.
333
M5Tough.VinVoltage.
333
M5Tough.VinCurrent.
333
M5Tough.VBusVoltage.
333
M5Tough.VBusCurrent.
333
M5Tough.BatChgCurrent.
333
M5Tough.BatPower.
333
M5Tough.AxpTemp.
333
M5Tough.ApsVoltage.
333
M5Tough.AxpState.
333
M5Tough.TftPower
power.
333
M5Tough.SpeakerPower
power.
333
M5Tough.SetBusPowerMode
mode.
334
M5Tough.PowerOff sec.
334
M5Tough.LightSleep
sec.
334
M5Tough.DeepSleep sec.
334
ANNEXCAM..
334
Functionalities enabled in the ANNEXCAM
version.
337
Camera Functions / commands.
338
Using AnnexCam in output page.
341
Control of the camera using
URL.
342
Face Recognition.
343
Image / video reception from
Annex.
344
ANNEXEPAPER for LILYGO T5 4.7” E-paper
module.
346
.........................................................................................................................................
347
GRAPHIC GUI for E-PAPER..
349
Functionalities enabled in the E-PAPER
version.
356
PEEK and POKE FUNCTIONS..
358
BAS.PEEK(addr).
358
BAS.PEEK16(addr).
358
BAS.PEEK8(addr).
358
BAS.POKE addr, data.
358
BAS.POKE16 addr, data.
358
BAS.POKE8 addr, data.
358
CONVERSION FUNCTIONS..
359
CONVERT.DEGC_TO_F(degC).
360
CONVERT.F_TO_DEGC(degF).
360
CONVERT.TO_IEEE754(num).
360
CONVERT.FROM_IEEE754(iee754_bin).
360
CONVERT.MAP(number, fromLow, fromHigh,
toLow, toHigh).
360
CONVERT.TO_BCD(number).
360
CONVERT.FROM_BCD(number).
360
CONVERT.LIMITS(number, min,
max).
360
BAS CONSTANTS..
360
BAS.VER.
361
BAS.VER$.
361
BAS.ERRLINE.
361
BAS.ERRNUM.
361
BAS.ERRMSG$.
361
BAS.FILENAME$.
361
BAS.RTCMEM$.
361
BAS.SSID$.
361
BAS.PASSWORD$.
361
BAS.LOAD.
361
BAS.RESETREASON.
362
BAS.WAKEUPREASON.
363
BAS.DEVICE.
364
BAS.TFT.
365
OPTION COMMANDS..
365
OPTION.BASE 0 | 1.
366
OPTION.CPUFREQ
80|160|240.
366
OPTION.ES8388.
366
OPTION.MAC mac$.
366
OPTION.LOWRAM value.
366
OPTION.NTPSYNC.
366
OPTION.WDT time.
366
OPTION.WDTRESET.
366
OPTION.WLOG value.
366
OPTION.TOUCH value.
367
OPTION.I2S BCLK_pin, WSEL_pin,
DOUT_pin.
367
OPTION.PSRAM limit.
367
HALL Sensor (Internal): 367
BAS.HALL.
367
FUNCTIONS: 367
NUMERICAL FUNCTIONS..
367
ABS(number) 368
ACOS(number) 368
ADC(pin) 368
APDS9960.SETUP (mode) 368
APDS9960.READGESTURE..
368
APDS9960.AMBIENT.
368
APDS9960.RED..
368
APDS9960.GREEN..
368
APDS9960.BLUE..
369
APDS9960.PROXIMITY..
369
APDS9960.GESTUREGAIN (gain) 369
APDS9960.GESTURELED (intensity)
369
ASC(string$) 369
ASIN(number) 369
ATAN(number) 369
ATAN2(x, y) 369
BAS.VER..
369
BAS.ERRLINE..
370
BAS.ERRNUM..
370
BME280.SETUP(address) 370
BME280.ALT(qnh) 370
BME280.HUM..
370
BME280.QFE..
370
BME280.QNH(altitude) 370
BME280.TEMP..
370
BNO055.SETUP( address) 370
BNO055.HEADING..
370
BNO055.PITCH..
370
BNO055.ROLL.
370
BNO055.VECTOR ( param, axis) 371
BNO055.CALIB [(param)] 371
CINT(number) 372
CONVERT.DEGC_TO_F(degC) 372
CONVERT.F_TO_DEGC(degF) 372
CONVERT.TO_IEEE754(num) 372
CONVERT.FROM_IEEE754(ieee754_bin)
372
CONVERT.MAP(number, fromLow, fromHigh,
toLow, toHigh) 372
CONVERT.TO_BCD(number) 372
CONVERT.FROM_BCD(number) 372
COS(number) 372
COUNTER.COUNT (cnt) 372
COUNTER.PERIOD (cnt) 372
DATEUNIX(date$) 372
DHT.TEMP..
373
DHT.HUM..
373
DHT.HEATINDEX..
373
DISTANCE(pin_trig, pin_echo) 373
EMAIL from$, to$, subject$,
message$.
373
ESPNOW.ADD_PEER(MAC_add$ [,interface]
[,channel]) 373
ESPNOW.BEGIN..
373
ESPNOW.DEL_PEER(MAC_add$) 373
ESPNOW.STOP..
373
ESPNOW.WRITE( msg$) 373
ESPNOW.WRITE( msg$,MAC_add$) 373
EXP(number) 373
FIX(number) 374
FILE.DELETE(filename$) 374
FILE.EXISTS(filename$) 374
FILE.SIZE(filename$) 374
FLASHFREE..
374
FUSION.ANGLE(axis) 374
INSTR([start], string$, pattern$)
374
I2C.LEN..
374
I2C.READ..
374
I2C.READREGBYTE (i2c_address, register)
375
I2C.END..
375
INT(number) 375
LEN(string$) 375
LOG(number) 375
MILLIS..
375
MQTT.Setup(server$ [,debug]) 375
MQTT.Certif(cert_pem$ [,client_cert_pem$]
[,client_key_pem$]) 376
MQTT.PSK(psk_hint_key$) 376
MQTT.LWT(topic$, message$ [,Qos,
[,retain]) 376
MQTT.Connect(login$, pass$ [,id$])
376
MQTT.Connect("", "" [,id$]) 376
MQTT.Disconnect[()] 376
MQTT.Publish(topic$, message$ [,Qos]
[,retain]) 376
MQTT.Subscribe(topic$ [,Qos])
376
MQTT.UnSubscribe(topic$) 376
MQTT.Connected[()] 376
MQTT.Status[()] 376
NEO.GETPIXEL(pos) 377
NEO.RGB(R, G, B) 377
PI 377
PID1.COMPUTE( current_value,
target_value) 377
PIN(pin_number) 377
PIN.TOUCH(pin_number) 377
PING(host$) 377
POW(x, y) 377
RAMFREE..
377
RFID.SETUP(CS_pin, RST_pin) 378
RFID.SETGAIN(gain) 378
RFID.SETKEY(key$) 378
RFID.RESET.
378
RFID.AWAKE..
378
RFID.SETNUID(NUID$) 378
RFID.WRITE(block, data$) 379
RND(number) 379
SERIAL.LEN..
379
SERIAL2.LEN..
379
SGN(number) 379
SIN(number) 379
SPI.BYTE(byte) 379
SQR(number) 379
TAN(number) 379
TFT.RGB(r,g,b) 379
TIMEUNIX(time$) 379
TM1638.BUTTONS..
380
TOUCH.X..
380
TOUCH.Y..
380
VAL(string$) 380
WIFI.CHANNEL.
380
WIFI.MODE..
380
WIFI.NETWORKS ( network$ )
380
WIFI.RSSI 381
WIFI.STATUS..
381
WORD.COUNT( string$ [,delimiter$])
381
WORD.FIND( string$, find$ [,delimiter$])
381
STRING FUNCTIONS..
382
BAS.ERRMSG$.
383
BAS.FILENAME$.
383
BAS.FTP$( host$, login$, password$,
file$, folder$) 383
BAS.PASSWORD$.
383
BAS.RTCMEM$.
383
BAS.SSID$.
383
BAS.VER$.
383
BIN$(number) 383
BUTTON$(name$, label [, id] )
383
CHECKBOX$( variable [,id]) 383
CHR$(number) 384
CSSID$(object_id, object_style)
384
DATE$[(format)] 384
ESPNOW.ERROR$.
384
ESPNOW.READ$.
384
ESPNOW.REMOTE$.
384
FILE.DIR$[(path$)] 384
FILE.READ$(filename$,[line_num] | [start,
length]) 384
HEX$(number) 384
HtmlEventButton$.
384
HtmlEventVar$.
385
IMAGE$(path [,id]) 385
IMAGEBUTTON$(path, label [,id])
385
IP$.
385
IR.GET$[ (param) ] 385
JSON$(string$, field$) 385
LCASE$(string$) 385
LED$(variable [,id]) 386
LEFT$(string$, num) 386
LISTBOX$(variable$, "option1, option2,
option3, ..." [, height] [,id]) 386
MAC$[ (id) ] 386
METER$(variable, min, max [,id])
386
MID$(string$, start [,num]) 386
MQTT.Message$.
387
MQTT.Topic$.
387
OCT$(number) 387
PASSWORD$(variable [, id] ) 387
REPLACE$(expression$, find$,
replacewith$) 387
RFID.NUID$.
387
RFID.TYPE$.
388
RFID.READ$(block [,key_b]) 388
RIGHT$(string$, num) 388
RTC.DATE$[(format)] 388
RTC.TIME$.
389
SERIAL.CHR$.
389
SERIAL.INPUT$.
389
SERIAL2.CHR$.
389
SERIAL2.INPUT$.
389
SLIDER$(variable, min, max [,step] [,id])
389
SPACE$(number) 389
SPI.STRING$(data$, len) 389
SPI.HEX$(datahex$, len) 389
STR$ (number [,format$ [,toint]])
390
STRING$(num, char$) 392
TEMPR$(pin_number [,ID]) 392
TEXTAREA$(variable [, id] ) 393
TEXTBOX$(variable [, id] ) 393
TRIM$(string$) 393
TIME$.
393
UCASE$(string$) 393
UDP.READ$.
393
UDP.REMOTE$.
393
UNIXDATE$(value [,format]) 393
UNIXTIME$(value) 393
URLMSGGET$ ([arg$]) 394
WGET$( http_server$, port [,header] )
394
WGET$( url$, [,header] ) 394
WGETRESULT$.
394
WORD$(string$, position [,delimiter$])
394
WORD.DELETE$(string$, position
[delimiter$]) 394
WORD.EXTRACT$(string$, lead$, trail$)
395
WORD.GETPARAM$( setting$,
parameter$ [,separator$]) 395
WPOST$(server$, body$, port [,header])
395
WPOST$(url$, body$, [,header])
395
COMMANDS: 395
AUTOREFRESH interval 396
BAS.LOAD filename$.
396
BAS.RTCMEM$ = val$.
396
CLS..
396
CSS style_code$.
396
COMMAND cmd$.
396
COUNTER.RESET cnt 396
COUNTER.SETUP cnt, pin [,mode]
396
CSSEXTERNAL file$.
397
DATA const1 [,const2] ... 397
DHT.SETUP pin, model 398
EMAIL.SETUP server$, port, user_name$,
password$ [, debug] 398
EMAILASYNC from$, to$, subject$,
message$.
398
FILE.FROMBASE64 source$,
dest$.
398
FILE.SAVE filename$, content$.
398
FILE.TOBASE64 source$, dest$.
398
FUSION.INIT.
398
FUSION.MADGWICK ax, ay, az, gx, gy,
gz.
399
FUSION.MADGWICK ax, ay, az, gx, gy, gz,
mx, my, mz.
399
FUSION.MAHONY ax, ay, az, gx, gy, gz, mx,
my, mz.
400
FUSION.BETA =.
400
FUSION.ZETA =.
400
FUSION.KI =.
400
FUSION.KP =.
400
HTML code$.
400
I2C.SETUP sda_pin, scl_pin [,freq ]
400
I2C.BEGIN address.
400
I2C.END..
401
I2C.REQFROM address, length.
401
I2C.READREGARRAY i2c_address, register,
nb_of_bytes, Array() 401
I2C.WRITE value.
401
I2C.WRITEREGBYTE i2c_address,register,
value.
402
I2C.WRITEREGARRAY i2c_address, register,
nb_of_bytes, Array() 402
INCR var [, increment] 402
INPUT.TIMEOUT timeout 402
INPUT["prompt$";] variable.
402
INTERRUPT pin_no, {OFF | label} [, mode]
403
IR.INIT pin_rx | OFF [, pin_tx]
403
IR.SEND type, code$, bits.
403
JSCALL javaCode$.
403
JSCRIPT script$.
403
JSEXTERNAL file$.
403
LCD.INIT address, cols, rows.
404
LCD.CLS..
404
LCD.PRINT x, y, text$.
404
LOCAL var1 [,var2], ... 404
MAXDISPLAY.SETUP CS_pin.
404
MAXDISPLAY.PRINT msg$ [,‘brightness]
404
MAXSCROLL.SETUP nb_devices,
CS_pin.
404
MAXSCROLL.PRINT msg$.
404
MAXSCROLL.NEXT msg$.
405
MAXSCROLL.TEXT msg$.
405
MAXSCROLL.SHOW pos [, brightness]
405
MAXSCROLL.SCROLL [brightness]
405
MAXSCROLL.OSCILLATE [brightness]
405
NEO.PIXEL led_pos, R, G, B [, disable]
405
NEO.PIXEL led_pos, COLOR [, disable]
405
NEO.SETUP pin [,nb_led] 405
NEO.STRIP led_start_pos, led_end_pos, R,
G, B [, disable] 405
NEO.STRIP led_start_pos, led_end_pos,
COLOR [, disable] 406
NEOSCROLL.SETUP nb_devices, pin
[,serpentine] 406
NEOSCROLL.PRINT msg$.
406
NEOSCROLL.NEXT msg$.
406
NEOSCROLL.COLORS col$.
406
NEOSCROLL. NEXTCOLORS col$.
406
NEOSCROLL.SHOW pos [, brightness]
406
NEOSCROLL.TEXT msg$.
406
NEOSCROLL.SCROLL [‘brightness]
406
NEOSCROLL.OSCILLATE [‘brightness]
406
OLED.CLS..
407
OLED.INIT orientation.
407
OLED.REFRESH fmt 407
OLED.COLOR color 407
OLED.PIXEL x, y.
407
OLED.LINE x1, y1, x2, y2.
407
OLED.RECT x,y, width, height [,fill]
407
OLED.CIRCLE x, y, radius [, fill]
407
OLED.FONT font_num..
408
OLED.PRINT x, y, text$ [background]
408
OLED.IMAGE x, y, image$.
408
OLED.BMP x, y, image$.
408
ONERROR ABORT or ONERROR IGNORE or
ONERROR SKIP [nn] or ONERROR CLEAR or ONERROR GOTO label
408
ONESPNOWERROR [label | OFF] 408
ONESPNOWMSG [label | OFF] 408
ONGESTURE [label | OFF] 408
ONHTMLCHANGE [label | OFF] 409
ONHTMLRELOAD [label | OFF] 409
ONINFRARED label 409
ONMQTT label 409
ONRFID label 409
ONSERIAL [label | OFF] 409
ONSERIAL2 [label | OFF] 409
ONTOUCH [label | OFF] 409
ONUDP [label | OFF] 409
ONURLMESSAGE [label | OFF] 409
ONWGETASYNC [label | OFF] 409
OPTION.CPUFREQ 80|160|240.
409
OPTION.LOWRAM value.
410
PAUSE delay.
410
PCA9685.SETUP addr 410
PCA9685.SETFREQ freq.
410
PCA9685.PWM pin, value.
410
PID1.INIT Kp, Ki, Kd.
410
PID1.LIMITS min, max.
410
PID1.PERIOD msec.
410
PID1.PARAMS Kp, Ki, Kd.
410
PID1.SETMODE mode.
410
PIN(pin_number) = val 410
PIN.DAC pin_number, value.
411
PIN.MODE pin_number, mode [,PULLUP |
PULLDOWN ] 411
PLAY.MP3 mp3$.
411
PLAY.STREAM stream$ [,buffer]
411
PLAY.SETUP dest [,buffer] [,mono]
411
PLAY.SPEAK message$ [, phonetic]
412
PLAY.STOP..
412
PLAY.VOICE "message", "language" [,
"filename"] [, action] 412
PLAY.VOLUME volume.
412
PLAY.WAV..
412
PRINT expression[[,; ]expression] ...
412
PRINT2 expression [[,; ]expression] ...
412
PWM.SETUP pin, chan, default,
[,freq] [,resol] 413
PWM.SETUP pin, OFF.
413
PWM.OUT chan, value.
413
READ var1 [,var2] ... 413
REBOOT.
413
REFRESH..
413
RESTORE [label] 413
RTC.SETTIME Year, Month, Day, Hours,
Minutes, Seconds.
413
SERIAL.BYTE ch1 [,ch2] . . . 413
SERIAL2.BYTE ch1 [,ch2] . . .
414
SERIAL.MODE baudrate [, bits, parity,
stop] 414
SERIAL2.MODE baudrate, pin_tx, pin
rx [, bits, parity, stop] [, TXbuffer, RXbuffer] 414
SETTIME Year, Month, Day, Hours, Minutes,
Seconds.
414
SLEEP value [,pin, level] 414
SOCKET client, msg$.
414
SPI.CSPIN pin [, polarity] 414
SPI.SETUP speed [,data_mode [,
bit_order]] 415
SPI.STOP..
415
ST7920.INIT CS_pin.
415
ST7920.CLS..
415
ST7920.REFRESH fmt 415
ST7920.COLOR color 415
ST7920.PIXEL x, y.
415
ST7920.LINE x1, y1, x2, y2.
415
ST7920.RECT x,y, width, height [,fill]
415
ST7920.CIRCLE x, y, radius [, fill]
415
ST7920.FONT font_num..
416
ST7920.PRINT x, y, text$ [background]
416
ST7920.IMAGE x, y, image$.
416
ST7920.BMP x, y, image$.
416
TM1637.PRINT msg$ [, brightness ]
416
TM1637.SETUP data_pin, clock_pin [,
bit_delay] [, display_type] 416
TM1638.PRINT msg$ [, brightness ]]
416
TM1638.SETUP data_pin, clock_pin,
strobe_pin.
416
TM1638.LEDS val 416
TFT.BMP filename$, [x, y [, back_color] ]
417
TFT.BRIGHTNESS val 417
TFT.CIRCLE x, y, radius,color [, fill]
417
TFT.FILL color 417
TFT.IMAGE filename$, [x, y [, back_color]
] 417
TFT.INIT orientation.
418
TFT.JPG filename$, [x, y [, scale] ]
418
TFT.LINE x1, y1, x2, y2, col 418
TFT.PIXEL x, y, col 418
TFT.PRINT expression [[,; ]expression]
... 419
TFT.RECT x, y, width, height, color [
[,fill] ,[round_radius] ] 419
TFT.SETFREQ freq.
419
TFT.TEXT.COLOR color [,backcolor]
419
TFT.TEXT.POS x, y.
419
TFT.TEXT.SIZE size.
419
TIMER0 interval, label 419
TIMER1 interval, label 419
TOUCH.CALIB..
420
UDP.BEGIN port 420
UDP.REPLY msg$ [,port] 420
UDP.STOP..
420
UDP.WRITE ip, port, msg$.
420
URLMSGRETURN msg$ [,content_type$]
420
WAIT.
420
WGETASYNC server$, port [,header]
420
WGETASYNC url$, port [,header]
421
WIFI.APMODE SSID$, password$ [, channel]
[, IP$ , MASK$] 421
WIFI.AWAKE..
421
WIFI.CONNECT SSID$, password$ [, BSSID$]
[, IP$ , MASK$ [, GATEWAY$]] 421
WIFI.POWER pow..
421
WIFI.SCAN..
421
WIFI.SLEEP..
421
WLOG [text$ | num] 422
WORD.DELPARAM setting$, parameter$,
[,separator$] 422
WORD.SETPARAM setting$, parameter$,
value$ [,separator$] 423
BASIC KEYWORDS..
423
CASE..
424
DIM array(size) [, …] 424
DO..
424
ELSE..
424
ELSEIF.
424
END [IF | SELECT | SUB] 424
ENDIF.
424
EXIT {DO | FOR | SUB} 424
FOR..
424
GOSUB [label | lab$] 424
GOTO [label | lab$] 424
IF.
424
LET var = expression.
424
LOOP..
424
NEXT.
424
OFF.
425
OUTPUT.
425
PULLUP..
425
PULLDOWN..
425
REM..
425
RETURN..
425
SELECT.
425
SPECIAL.
425
STEP..
425
SUB..
425
THEN..
425
TO..
425
UNTIL.
425
WEND..
425
WHILE..
425
Annex32 WI-Fi RDS (Rapid Development Suite) is
a version of the "BASIC" language developed to run on low cost
ESP-32 WIFI devices.
Annex32 is specifically for the ESP32 range of
devices, whose implemented features can vary greatly.
To offer some standardisation, Annex32 caters
in particular to M5stack devices, which include a micro-SD card
slot, TFT display, speaker, 3 user buttons plus a reset button, and
a lipo battery, all self-contained in a plastic case offering
expansion pin access and designed to accept ‘stackable’ expansion
modules.
All drivers needed for the M5stack features
are already included in the Annex32 firmware, and pre-configured
for the M5stack so that features such as TFT display and SDcard
work by default.
Similar functionality could be built using
alternative TFT display and SD card reader etc, if preferred.
Please refer to the original
M5Stack schematics for more details.
However, M5stack and its hardware features
merely offer a convenient standardised feature set, they are not
mandatory - Annex32 works with any ESP32 devices, with or without
hardware expansion modules.
Obviously appropriate hardware is needed for
any required features - eg: an OLED display could be used, but
scripts written for TFT displays will need modifying for the
different display.
Annex32 can use the internal flash disk space,
or an external SD card.
The internal and the external (SDcard) space
are mutually exclusive and cannot be accessed at the same time.
By default Annex32 will use the SD, if
available, otherwise it will use the internal flash disk space
(FATFS).
Both use the same type file system (FAT32),
enabling the use of long file names and directories.
Depending on the module flash memory size (4,
8 or 16MB), the internal disk space can be from ~1MB to 13MB.
Using the ESP32 partition scheme it is
possible to freely define this space, but modifying it will wipe
out all existing files already stored.
Annex32 Wi-Fi RDS takes from the original
concept of Annex WI-FI RDS for ESP8266 from which it shares
essentially the IDE interface and the same command syntax as much
as possible.
It should be straightforward switching to
Annex32 if coming from Annex, and the same programs should run
without (or with minimum) modifications (eg: pin numbers).
Annex32 Wi-Fi RDS benefits from the powerful
H/W architecture of the ESP32 using both cores and the RAM memory
available. In addition, for modules equipped with PSRAM memory
extension, Annex32 can make available to the users this additional
RAM space (up to 4MBytes).
Functionalities:
-
Includes an internal IDE so can be programmed directly using your
web browser (even from your phone/tablet) without any additional
utility.
-
Syntax highlighting with context-sensitive Help
-
A programmable web server which includes a file server
-
Supports OTA (over the air) update.
-
Support async events (interrupts, timers, web access, UDP, ….)
-
Breakpoints, immediate execution of commands, display of variables,
single step.
-
A basic interpreter with floating point variables (double
precision) and string variables, multi-dimensional arrays (float
and string), user defined subroutines.
-
Access to any available I/O pin for input/output, PWM and
Servo.
-
Errors Handling .
-
Support TCP (HTTP) GET and POST for communications
-
Support for UDP for communications.
-
Support for sending Emails using SMTP SSL servers
-
Support for AJAX communications (GET, POST, PUT) Synchronous and
Asynchronous
-
Support for ESP-NOW communications
-
Support for MQTT communications
-
Support for MODBUS communications
-
Support for FTP communications
-
Support for Bluetooth Low Energy (BLE) communications
-
Support for Telegram communications
-
Support for RJ45 wired ethernet using W5500 module
-
Accompanying utility suite includes Flasher, File Manager, HTML
Converter, Backup/Restore to bin or zip, integrated Serial Port
Monitor, OTA (over the air) update server and UDP Console.
-
IMU / AHRS Fusion algorithms 6 DOF and 9 DOF (Madgwick and
Mahony)
-
Play MP3 or WAV sound files or streaming using a speaker or an
external I2S DAC
-
Text to Speech using a speaker or an external I2S DAC
-
Support for regular expressions (regex)
The following
devices are supported directly with dedicated commands / functions
:
-
DHT11, DHT21 or DHT22 Temperature / Humidity Sensors
-
DS18B20 Temperature sensor
-
LCD HD44780 with I2C interface module (1, 2 or 4 lines with 16 or
20 chars per line)
-
LCD Display based on chipset ST7920 with 128x64 pixels
monochrome
-
OLED Display based on chipset SSD1306 or SH1106 with 128x64 pixels
monochrome
-
TFT Display at 16 bits colors based on the following chipset:
-
ILI9341 with 320x240 pixels
-
ILI9163 with several resolutions
-
ST7735 with several resolutions
-
ST7796 with 480x320 pixels
-
ILI9481 with 480x320 pixels
-
ILI9486 with 480x320 pixels
-
ILI9488 with 480x320 pixels
-
ILI7789 with several resolutions
-
SSD1351 with 128x128 pixels
-
GC9A01 with 240x240 pixels
-
TM1637 4 and 6 digits 7-segments display
-
TM1638 8 digits 7-segments display including 8 leds and 8
buttons
-
MAX7219 8 digits 7-segments display
-
MAX7219 8x8 dot matrix display modules
-
Neopixel WS2812 led strips
-
Neopixel WS2812 8x8 dot matrix display
-
PCA9685 PWM/SERVO module
-
Infrared interface with many RC protocols (transmission and
reception)
-
RTC module (DS1307 or DS3231)
-
HC-SR04 ultrasonic sensor for distance measurement
-
BNO055 Absolute Orientation Sensor
-
MPU9250 / MPU6500 IMU units
-
MPU6886 IMU unit
-
BME280 Combined humidity and pressure sensor
-
BME680 Combined gas, pressure, temperature & humidity
sensor
-
HDC1080 High Accuracy Digital Humidity Sensor with Temperature
Sensor
-
CCS811 Air Quality Sensor
-
APDS9960 Digital Proximity, Ambient Light, RGB and Gesture
Sensor
-
W5500 RJ45 wired Ethernet interface
-
VL53L0X TOF (Time Of Flight) Distance Sensor
-
RFID MFRC522 cards reader
-
HX711 - Weight Measurement Module
-
SI5351 Clock Generator Module
-
Any compatible I2S DAC
-
Lora SX127x modules
-
STEP Motors
-
VGA output for ESP32–S3
-
RGB TFT output for ESP32-S3
Many ESP32 modules
/ units are supported and can be configured using the
“CONFIG” menu:
-
Almost all the ESP32 modules including ESP32 devkit, ESP32 wemos
mini, ESP32 lolin lite, ...
-
M5Stack
-
M5 Atom
-
M5 Atom matrix
-
M5 Atom Echo
-
ESP32-CAM
-
M5CAMERA
-
ODROID GO
-
M5Tough
-
WIFI LORA 32
-
ESP32-2432S028 (module with a 240x320 2.8” TFT with resistive
touchscreen)
-
ESP32-3248S035R (module with 320x480 3.5” TFT with resistive
touchscreen)
ESP32-3248S035C (module with 320x480 3.5” TFT with capacitive
touchscreen)
In addition to
the ESP32, Annex now extends its support to other family members,
including the ESP32-C3, ESP32-S2, and ESP32-S3. This support
encompasses both direct USB connection and variants with USB to
serial chip. For all of these modules, Annex offers compatibility
across different versions, considering the specific type of flash
memory installed, including DIO, QIO, and OPI, as well as the
presence of PSRAM, with* options for QIO and OPI configurations. As
a result of this diverse range of variants, Annex provides distinct
firmware releases tailored to each particular configuration. This
ensures optimal performance and seamless integration across the
ESP32 series.
The following
modules equipped with ESP32-S3 are also supported and can be
configured using the “CONFIG” menu:
-
ESP32-4848S040 (module with a 480x480 4” RGB TFT with capacitive
touchscreen)
-
ESP32-8048S070C (module with a 800x480 7” RGB TFT with
capacitive touchscreen)
The basic interpreter works by reading a
script file saved to the esp local disk filing system.
This is the default mode if no external
SDcard(s) are connected to the ESP32.
In addition, Annex32 can use an external
SDcard as file system permitting up to 16Gbytes of disk space.
During the startup, if an external SDcard is
detected it will be automatically connected and used as the default
file system, in which case the internal filing system will not be
used.
Because the ESP32 contains a good quantity of
RAM, the user script is copied from the disk into a dedicated
area in the RAM memory where it is executed, together with the list
of the program lines, the branch labels and the list of the user
defined subroutines..
This uses more RAM compared to other
approaches, but allows faster program execution.
Another performance consideration is that the
ESP32 must be capable of executing several activities in the
background (web server, file server, etc..) so needs sufficient
free memory for running such tasks, and those parallel tasks will
obviously have an impact on script performance..
So performance-wise, the interpreter is not
particularly fast, but it should be fast enough for most tasks you
may require. In particular it is around 2 times faster than Annex
for ESP8266, considering that many tasks can run in parallel
without any appreciable performance impact (such as playing music
in the background).
Basic program lines :
A typical script line should comply with the
following syntax :
[label:]
command [argument1 [,argument2 …..]]
Script lines may contain several commands on
the same line if separated by the colon character ":".
[label:]
command1 [argument1 [,argument2 …..]]: command2 [argument1
[,argument2 …..]]
It must be noted that use of several commands on the same line is
not recommended and will cause program errors if the line contains
GOSUB or user defined subroutine calls.
All program jumps (eg: GOTO, GOSUB) are
referenced by their branch label names - line numbers are not
referenced in scripts, they are merely available in the editor as a
programming convenience if wished, and for error references.
NOTE : The gosub and the call to user
defined subroutines must be used alone on the script line.
Branch labels should not be named the same as
a command name, and must follow the same format as variables (see
below).
A branch label definition must begin the line,
and a colon (":") must terminate the label definition.
Any references to the defined label (GOTOs and
GOSUBs etc) do not use a colon.
Example :
b
= 10
a
= 20 : c = 30
GOSUB LABEL1
END
LABEL1:
print "Label1"
RETURN
|
The interpreter has 2 types of variables:
-
Floating Point (double precision)
-
String
Floating point variables can store numbers
with decimal points; they can also store integer numbers with a
precision equivalent to 32bits.
Strings contain sequences of characters
(example "my program") and must be terminated by "$".
The strings are not limited in size, they are
only limited by the amount of memory available.
NOTE: The string variables cannot contain
the character with ASCII code 0 (zero) because it is used
internally as an end of string delimiter.
The variables are defined as any name starting
with an alpha character (a, b, ..z) followed by any alphanumeric
character (a..z, 0..9); it can also include the "_"
(underscore).
The case is don’t care, so ‘’Num"
is equivalent to "nuM".
The variable name length is limited to 31
characters maximum, including the "$" for the strings.
There are no limits in terms of number of
variables; the only limit is the RAM memory available.
Example:
NUM = 10.56
myString$ = "this is My
String"
this_is_my_value$
= "ESP8266"
number = 8826621
|
Numeric variables and string variables are
managed separately so the same name can be used; this means that
A and A$ are different variables that can coexist at
the same time (even if this could lead to confusion).
Constants:
The numeric constants can have the following
format :
A =
5 : Z = 1.5
B =
1.23456E5 -> same as 123456
C =
1.23456E+5 -> same as 123456
D =
1.23456E-3 -> same as 0.00123456
The string constants are simply defined as a
text between quotes:
A$
= "This
is my string" : B$ =
"another string"
The strings can include the character "
(quote) simply typing it two times :
A$
= "this
is ""MY"" string"
The | (vertical bar) can also be used
as a string literal.
This permit to include the " (quote) easily
inside a string constant :
A$
= |this
is a "string" constant|
The hexadecimal constants can be defined
simply prefixing it with &h :
E
= &hABCD -> equivalent of decimal
43981 (hexadecimal constant)
F
= &hA0 -> equivalent of decimal
160
The binary constants can be defined simply
prefixing it with &b :
E
= &b00000101 -> equivalent of decimal
5 (binary constant)
F
= &b10000001 -> equivalent of
decimal 129
The octal constants can be defined simply
prefixing it with &o :
E
= &o377 -> equivalent of decimal 255
(octal constant)
F
= &o17 -> equivalent of decimal
15
Arrays are defined using the DIM
command.
Their names follow the same rules as the
regular variables and are followed by parenthesis (brackets)
containing the index. The subscript starts from 0, but you can
adjust the lower limit using
The scope of the Arrays is always global (see
next paragraph).
Example:
DIM
A(100)
define a floating point array with 101 elements (index
from 0 to 100)
DIM
ABC$(50)
define a string array with 51 elements
(index from 0 to 50)
A(15) = 1234.5678
ABC$(49) = "Hi friend!"
The arrays can have up to 5 subscripts
(dimensions), examples:
DIM
A(50,50) -> create a floating point array with
51*51 elements (2601)
DIM
J$(4, 4, 4) -> create a string array with 5 * 5
* 5 elements (125)
If the command
OPTION.BASE 1
is executed the subscripts start from 1 and an error will be raised
when trying to use the index 0.
This line
OPTION.BASE 1
must be present in the code before array declaration.
In this case
DIM
A(100)
define a floating point array with 100 elements (index
from 1 to 100)
DIM
ABC$(50)
define a string array with 50 elements
(index from 1 to 50)
Notice that declaring a multi-dimensional
array with multiple subscripts uses elements for every
possible[1] [2] [3] combination of
subscripts, whereas in practice it may be preferable to declare
multiple arrays with the same subscript, eg:
users=4
DIM Name$(users)
DIM Address$(users)
DIM Tel$(users)
Which only uses 5 + 5 + 5 elements
(15)
NOTE:
The numerical Arrays are always
initialised at 0 with the command DIM.
The string Arrays are always initialised
as null string with the command DIM.
There are no limits to the number of
arrays or their size, the only restriction is the RAM memory
available.
The arrays can be re-dimensioned using the
same command DIM.
In this case all the existing elements will
maintain the previous value except the new elements that will be
initialised at 0 or null string.
Example :
DIM A(5)
' all the elements are initialised at
0
A(0)
= 123
Print A(0)
' print 123
Dim A(10)
Print A(0)
' print the same value
123
Print A(10)
' print 0
|
In addition the elements of the arrays can be
initialised with a given value during the command DIM.
Example :
DIM
A(5) = 0, 1, 2,
3, 4, 5 ' set A(0)= 0,
A(1)= 1, A(2)=2, ….
If the command
OPTION.BASE 1
is executed before
DIM
A(5) = 0, 1, 2,
3, 4, 5 ' set A(1)= 0,
A(2)= 1, A(3)=2, ….
The same can be done with string arrays.
Example :
DIM
A$(5) =
"zero", "one", "two", "three", "four", "five"
Two additional functions can be used to determine the bound limits
of arrays:
LBOUND(array()
[, dimension]) : Returns the lower bound of the
specified array dimension.
UBOUND(array()
[, dimension]) : Returns the upper bound of the
specified array dimension.
Example :
OPTION.BASE 0
DIM A(100)
print
LBOUND(a()) ' print 0
(option.base 0)
print
UBOUND(a()) ' print
100
OPTION.BASE 1
DIM A(10,
20)
print
LBOUND(a(), 1)
' print 1 (option.base
1)
print
LBOUND(a(), 1)
' print 1 (option.base
1)
print
UBOUND(a(), 1)
' print 10
print
UBOUND(a(), 2)
' print 20
|
Variables and arrays defined in the main code
are global, therefore any variable is accessible from any part of
the code after it has been previously defined there.
Variables and arrays defined inside
“user defined” subroutine (SUB) are visible only inside that sub
and inside all the code called by that subroutine; their content
(and their memory space) is removed at the end of the SUB
The LOCAL command permits defining local
variables inside of "user defined" subroutines; this permits to use
the same name of an “already existing” variable locally without
modifying the original.
As for all the variables defined inside SUB,
they will disappear at the end of the subroutine.
Example:
A
= 10
B
= 20
C
= 30
mysub
"Hello"
PRINT A,B,
C
END
SUB mysub(a$)
LOCAL A,B
A
= 123
B
= 456
C
= 789
D
= 8888
PRINT A$, D
END
SUB
|
In this example, calling the user-defined
subroutine "mysub" will not modify the content of the global
variables A and B (defined locally) but will modify the content of
the variable C (not defined locally) and the variable D will
disappear at the end of the SUB.
The keywords recognized by the interpreter can
be defined into 3 classes:
●
Operators
●
Commands
●
Functions
The Operators are symbols that tell the
compiler to perform specific mathematical or logical
manipulations.
Commands and Functions both execute an action,
but functions also return a data value.
For example
PRINTis a command and
SIN()
is a function whereas the ‘+’ in a = b
+ 5 is an operator.
The string functions are always followed by
the "$" symbol if they return a string value.
In addition to commands and functions there
are all the internal interpreter internal commands that are part of
the language itself.
The following operators are available. These
are listed in the following tables by order of precedence.
Operators on the same line are processed with a left to right
precedence.
Arithmetic operators:
^
|
Power
|
* / \ MOD
|
Multiplication,
division, integer division and modulo (remainder of the
division)
|
+ -
|
Addition and
subtraction
|
Shift operators:
x <<
y
x >> y
|
These operate in a
special way. << means that the value returned will be the
value of x shifted by y bits to the left while >> means the
same only right shifted. They are integer functions and any bits
shifted off are discarded and any bits introduced are set to
zero.
For more
information about the kinds of bitwise shifts, see Bitwise shifts.
|
Logical operators:
<> <
> <=
>=
=
|
Not Equal, less
than, greater than, less than or equal to,
greater than or
equal to, equal
|
AND OR NOT XOR
|
Conjunction,
disjunction, negation, Exclusive OR
|
String operators:
<> <
> <=
>=
=
|
Not Equal, less
than, greater than, less than or equal to,
greater than or
equal to, equal
|
+ &
|
Add strings
together
|
Bitwise operators:
AND OR XOR NOT
|
Binary AND, binary
OR, binary exclusive OR, binary negation
For more
information about the bitwise operators, see
Bitwise Operators
|
The operators AND, OR and XOR are integer
bitwise operators. For example PRINT (3 AND 6) will output 2.
Expressions beginning with open parenthesis
‘(‘ are always considered numerical but the parser is able to
determine if an expression is true or false even if the expression
represents a string.
Each expression representing a comparison,
returns a numerical value of 1 if the expression is true or 0 if
false.
For example 10 = 10 represents a value of 1
whereas 10 = 5
represents a value of 0.
The same logic is applied for string
expressions where "abc" =
"abc" represents a value of 1 and "abc" = "def" represents a
value of 0.
This is very useful in the IF command and also
in other expressions.
For example the following code :
A$ = "on"
If
A$ = "on"
then
pin(4)
= 1
Else
pin(4)
= 0
End
if
|
Can be replaced by
pin(4)
= (a$ =
"on")
The strings can also be compared to determine
the alphabetical order.
To see whether a string is greater than
another, Annex uses the so-called “ASCII” order.
In other words, strings are compared
letter-by-letter.
For example:
("Z"
> "A") is true
("Glow"
> "Glee") is true
("Bee"
> "Be") is true
("Bas"
> "Bat") is false
The algorithm to compare two strings is
simple:
Compare the first character of both
strings.
If the first character from the first string
is greater (or less) than the other string, then the first string
is greater (or less) than the second. We’re done.
Otherwise, if both strings’ first characters
are the same, compare the second characters the same way.
Repeat until the end of either string.
If both strings end at the same length, then
they are equal. Otherwise, the longer string is greater.
In the examples above, the comparison
"Z" > "A" gets
to a result at the first step while the strings"Glow" and "Glee" are compared
character-by-character:
-
G is the same as G.
-
l is the same as l.
-
o is greater than e. Stop here. The first string is greater.
The comparison algorithm given above is
roughly equivalent to the one used in dictionaries or phone books,
but it’s not exactly the same.
For instance, case matters. A capital letter
"A" is not equal to the lowercase "a". Which one is greater?
The lowercase "a". Why? Because the lowercase
character has a greater index in the ASCII table.
The IF can have the following syntax :
1) IF
expression THEN statement
2) IF
expression THEN statement1 ELSE statement 2
3) IF
expression THEN
Statements
ELSE
Statements
END IF
4) IF
expression THEN
Statements
ELSEIF expression THEN
Statements
ELSEIF ……..
………
ELSE
Statements
END IF
Example:
IF a
> 100 THEN print
"a"
IF b
<a THEN print
"b" ELSE print
"a"
IF c
> d THEN
print "C"
print "is greater"
ELSE
print "D"
print "is greater"
END
IF ' (can also be ENDIF
without space between END and IF)
IF d
= a THEN
print "d"
print "is like a"
ELSEIF d
= b
print "d"
print "is like b"
ELSEIF d
= c
print "d"
print "is like c"
ELSE
print "d"
print "is unknown"
END
IF ' (can also be ENDIF
without space between END and IF)
|
When the conditional is all on one line it
does not need terminating with an END IF
Example
IFa=2
THEN
PRINT "ok"
ELSE PRINT "not
ok"
The AND ,
OR keywords can be used
between the expressions as long as they are in
parenthesis.
Example:
IF
(a=1)
AND (b=2)
THEN PRINT "ok"
Or
IF
((a=2)
AND (b=3)
AND (c =
3)) OR (d=4)
THEN PRINT "ok"
The IF can be nested
Example:
IF
a=2 THEN
IF b = 2
THEN
IF c = 3 THEN
PRINT "ok"
END IF
END IF
END
IF
|
The “THEN” keyword can eventually be
removed, even if this is not recommended.
Example:
IF
a > 100 print
"a" else print
"b"
The FOR loop can
have the following syntax :
FOR
variable=init_value to end_value [step
value]
Statements
NEXT
variable
The ‘step’ value can be positive or
negative
Example:
FOR
i=1 to
5
Print i
NEXT
i
Will print 1, 2, 3, 4, 5
FOR
i=1 to 3
step 0.5
Print i
NEXT
i
Will print 1, 1.5, 2, 2.5, 3
FOR
i=3 to 1
step -0.5
Print i
NEXT
i
Will print 3, 2.5, 2, 1.5, 1
The command
EXIT
FOR can be
used to exit from the loop at any time:
FOR
i=1 to
50
IF
i=10 THEN
EXIT FOR
Print i
NEXT
i
Print
"end of loop"
Optionally, the variable in the
NEXT statement
can be omitted.
This means that this program is valid :
FOR
i=1 to
5
Print i
NEXT
The WHILE
WEND loop can have the following
syntax :
WHILE
expression
Statements
WEND
The loop is iterated as long as the expression
is true
Example:
i
= 0
WHILE
i < 3
Print i
i = i +
1
WEND
Will print 0, 1, 2
The DOLOOP
can have one of the following 4 syntax :
DO
WHILE expression
Statements
LOOP
DO
UNTIL expression
Statements
LOOP
DO
Statements
LOOP
WHILE expression
DO
Statements
LOOP
UNTIL expression
The command
EXIT
DO can be used to exit from the
loop at any time
Example
i
= 0
DO
Print
i
i = i
+ 0.5
LOOP
UNTIL i >3
Will print 0, 0.5, 1, 1.5, 2, 2.5, 3
i
= 0
DO
Print
i
i = i
+ 0.5
IF
i > 2 THEN
EXIT DO
LOOP
UNTIL i >3
Will print 0, 0.5,
1, 1.5, 2
The SELECTcan
have the following syntax:
SELECT
CASE expression
CASE exp1 [: Statements]
Statements
CASE exp2 TO exp3 [: Statements]
Statements
CASE exp4 [,exp5],
... [: Statements]
Statements
CASE ELSE
Statements
END
SELECT
Example:
a =
4
SELECT
CASE a
CASE 1
PRINT "case
1"
CASE 2 :
PRINT "case 2"
CASE 3 :
PRINT "case 3" : PRINT "can continue
on same line"
CASE 4 :
PRINT "case 4"
PRINT "can continue
also on next line"
CASE ELSE:
PRINT "case
else"
END
SELECT
Multiple cases:
a =
4
SELECT
CASE a
CASE1 :
PRINT "case 1"
CASE 2, 3, 5
: PRINT "case 2 or 3 or 5"
CASE4 :
PRINT "case 4"
CASE 6 TO
8 : PRINT "case 6 to 8"
CASE 9 TO 20
: PRINT "case 9 to 20"
CASE ELSE:
PRINT "case
else"
END
SELECT
The SELECT CASE can
also handle string content:
SELECT
CASE a$
CASE "a"
:
PRINT "case
a"
CASE "a",
"b", "c",
"d" :
PRINT "case a, b,
c, or d"
CASE "e"
TO "h" :
PRINT "case e to
h"
CASE ELSE:
PRINT "case
else"
END
SELECT
The GOTOcan have
the following syntax :
GOTO
[LABEL | LAB$]
Example
a =
5
IF a
> 5 THEN GOTO
LABEL1
END
....
LABEL1:
PRINT
"This is label1"
....
The goto must be
considered as an obsolete command and is provided just for backward
compatibility with old style Basic programs.
The GOSUBcan have
the following syntax :
GOSUB
[LABEL | LAB$]
The called function
must terminate with the command RETURN
Example
a =
5
IF a
> 5 THEN GOSUB LABEL1
END
....
LABEL1:
PRINT
"This is label1"
RETURN
The command DATA is used to store constant
information in the program code, and is associated with the command
READ. Each
DATA-line can contain one or more constants separated by commas.
Expressions containing variables will be also evaluated
here.
The goal of the DATA is to avoid repetitive
variable assignation lines, in particular for arrays.
The DATA values will be read from left to right, beginning with the
first line containing a DATA statement. Each time a READ
instruction is executed the saved DATA position of the last READ is
advanced to the next value. Strings must be written in quotes like
string constants. The command
RESTORE resets the pointer of the current DATA
position, so the next READ will read from the first DATA found from
the beginning of the program.
In case READ uses the wrong variable type the error message
"Type mismatch" appears while referring to the line number
containing the READ statement that triggered the condition.
DATA lines may be scattered throughout the
whole program code, but for the sake of clarity they would be
better kept together at the beginning of the program.
The DATA can have
the following syntax :
DATA const1
[,const2] …..
The constants can
be Numerical or String.
Example :
DATA 1, 55.88,
"constant", 99
READ A, B, C$,
D
PRINT A, B, C$,
D
Example without
DATA:
dim colors$(5)
colors$(1)
= "Red"
colors$(2)
= "Green"
colors$(3)
= "Blue"
colors$(4)
= "Yellow"
colors$(5)
= "Magenta"
Same example but
using DATA:
DATA
"Red", "Green",
"Blue", "Yellow", "Magenta"
dim colors$(5)
For i=1
to 5
Read colors$(i)
Next i
The command
RESTORE can optionally define a label to set the DATA
pointer to a specific point
Example
data 0, 1, 2,
3, 4, 5
block2:
data 10, 11,
12, 13, 14, 15
block3:
data 20, 21,
22, 23, 24, 25
block4:
data 30, 31,
32, 33, 34, 35
restore block3
for z
= 0 to 5
read a
print a,
next z
restore block2
print
" "
for z
= 0 to 5
read a
print a,
next z
print
"----------"
Define the end of
the program. With this command the program stops.
It can also be
:
END IF -> close the IF command
END SELECT -> closes the SELECT CASE command
END SUB -> closes the user defined SUB
Permit to exit from
a loop or a user defined SUB.
The syntax is :
EXIT
DO -> exit from a DO loop
EXIT
FOR
-> exit from a FOR loop
EXIT
SUB
-> exit from a user defined SUB.
Define a
user-defined subroutine, which the script can use like a command or
function.
User-defined
subroutines are effectively additional commands, so cannot be used
as branch labels.
Permit to create a
user defined command with optional parameters.
The syntax is SUB
subname[(arg1 [,arg2] …)]
The variables are
passed by reference; this means that the arguments, if modified
inside the subroutine, will modify the original variable. This can
be useful to return values from the subroutine (acting like a
function).
It is possible to
pass arrays using the syntax array_name().
Using the
LOCAL command will permit to define local variables (useful
to avoid to modify existing global variables).
Example 1 :
routine cube
SUB
cube(x)
PRINT X ^3
END
SUB
cube 3 ' will print 27
|
Example 2:
routine cube with returning argument
SUB
cube(x,y)
y = x ^3 ' the value is returned using the 2nd
argument
END
SUB
ret
= 0
cube 5,
ret
PRINT
ret
' will print 125
|
Example 3:
routine with local variables and returning argument
SUB left_trim(s$,
ret$)
LOCAL i
i
= 1
DO UNTIL i =
len(s$)
IF
mid$(s$, i, 1) <>
" " THEN EXIT
DO
i = i + 1
LOOP
ret$ = mid$(s$,
i)
END
SUB
z$
= ""
FOR i
= 1 to 3
left_trim " remove space from
left ", z$
PRINT z$ + "--"
NEXT i
|
Will print
remove space from
left --
remove space from
left --
remove space from
left --
As you can see in
this example, the variable i in the FOR loop is not modified by the
LOCAL variable i in the subroutine.
Example 4:
pass arrays
SUB pass_array(f(),
c$())
Dim myArray(10)
myArray(0)
= 456
Print f(0),
c$(0), myArray(0)
f(1) =
123
c$(1) =
"myText"
END
SUB
Dim alpha(10)
Dim beta$(10)
alpha(0)
= 456
beta$(0)
= "testme"
Pass_array
alpha(), beta$()
Print alpha(1),
beta$(1)
|
In this example,
the array alfa() is passed locally to the array f()
and the array beta$() is passed locally to the array
c$().
Modifying locally
these arrays change the value of the original one as their content
is passed by reference.
The array
“myArray” will disappear at the end of the SUB
As the numerical
variables are stored internally as double precision floating
numbers, it is possible to store numbers with a precision
equivalent to 32 bits.
Several boolean
operators are available to manipulate these numbers..
The first operator
is the bit shift; it can be shift left
<< or shift right >>
This operator
permits to shift the number of a specified number of positions to
left or right.
Example
A
= 1
Print A
<< 3 ' will print 8
A
= 16
Print A
>> 2 ' will print 4
The operators
AND
,
OR ,
XOR are also available :
A
= 24
A
= 15
Print A
AND B '
will print 8
A
= 24
A
= 15
Print A
OR B '
will print 31
A
= 24
A
= 15
Print A
XOR B '
will print 23
The unary operator
NOT
is also available. It inverts all the bits from 0 to 1:
A
= 0
Print
Hex$(NOT A)
' will print
FFFFFFFF
For a 32 bits
number, assuming 4 bytes ABCD where A is the MSB and D the LSB, the
bytes can be extracted as follows :
VAR
= &h12345678 ' this is a 32 bits variable
D = VAR
AND &hFF
C = (VAR
>> 8) AND
&hFF
B = (VAR
>> 16) AND
&hFF
A = (VAR
>> 24) AND
&hFF
For more
information, see
Bitwise
Operators
Annex allows to control and manage errors that
occur during the execution of the code.
This is managed with the command ONERROR.
This command defines what action is taken when
an error occurs, and applies to all errors, including syntax
errors.
It can be used in different ways, as specified
in the table below:
FUNCTIONS / COMMANDS
|
DESCRIPTION
|
|
Displays the error message then aborts the
program.
This is the normal behaviour and is the
default when a program starts running.
|
|
Any error will be simply ignored.
As this can make it very difficult to debug a
program it should be used wisely.
|
|
Ignore an error in the next command(s)
executed after the current command (the number of skipped commands
depends on whether the number ‘nn’ is specified).
'nn' is optional, the default is 1
if not specified.
After the number of skipped commands has
completed (with an error or not) the behaviour will revert to
ONERROR ABORT.
|
|
Reset the eventual pending error
|
ONERROR
GOTO [label
| OFF]
|
Jumps to the error handling routine defined by
the label.
It can be removed (hence reverting to ONERROR
ABORT) replacing the label with OFF.
Using RETURN inside the error handling routine
will continue the execution on the line following the error.
|
When an error occurs, the following constants
are available :
CONSTANT
|
DESCRIPTION
|
|
Returns the line
number where the error happened. Value of 0 means no error.
It is reset to 0
with the command ONERROR CLEAR or running the program or with
the command ONERROR IGNORE or ONERROR SKIP.
|
|
Returns a number
where non zero means that there was an error.
It is reset to 0
with the command ONERROR CLEAR or running the program or with
the command ONERROR IGNORE or ONERROR SKIP.
|
|
Return a string
representing the error message that would have normally been
displayed on the console. It is reset to “No Error” running the
program or with the command ONERROR CLEAR or ONERROR IGNORE or
ONERROR SKIP.
|
Example of error handling using the command
ONERROR GOTO :
ONERROR
GOTO
Error_Handler
Print
"start"
Print
3/0
' this generates a divide by zero
error
Print
space$(60000)
' this generates an out of memory
error
End
Error_Handler:
Print
"Error text
"; BAS.ErrMsg$
Print
"Error
num "; BAS.ErrNum
Print
"Error line
"; BAS.ErrLine
Return
' returns to
the line following the error
|
When a client
connects to the module using its IP address, the module will
redirect automatically to the url ‘/output?menu’, which sends an
empty html page present on the module.
That page contains
a bunch of javascript code permitting to interface the page with
the module using javascript.
This page will
automatically open a websocket connection with the module; the
"squared led" indicates if the connection was successful (green) or
not (red).
A mechanism of ping
- pong has been implemented into the javascript in order to hold
the connection alive all the time. If the connection is lost, the
page will try to reconnect automatically without any manual
action.
The button
"reconnect" permits to force the reconnection if the automatic
reconnection fails.
As soon as the
connection is done with the module, the html page is ready to send
and receive messages to / from the module.
Initially the page
is empty but its content can be easily filled.
To send HTML code
to the page, the command HTML is used.
The syntax is :
HTML HTML code.
For example the
line
HTML
"Hello, world <br>This is my first html
content<br>"
Will give this
result :
Continuing with the
HTML command, the content can be improved :
HTML
"Textbox: <input
type='text'><br>"
Continuing
again:
HTML
"Button: <button type='button'>Click
Here</button>"
All the html code
can be combined and sent with just one HTML command; this is much
faster:
a$ =
"Hello, world <br>This is my
first html content<br>"
a$ = a$
+ "Textbox: <input
type='text'><br>"
a$ = a$
+ "Button: <button type='button'>Click
Here</button>"
HTML
a$
|
To clear the
content of the page, the command is:
CLS
Now we can try
another example
CLS
a$ =
"Now style me,
please<br>"
a$ = a$
+ "Button1: <button id='but1'
type='button'>ON</button> "
a$ = a$
+ "Button2: <button id='but2'
type='button'>OFF</button>"
HTML
a$
Now we will try to
style the buttons using css.
This can be done
using command CSS CSSID$()
For example the
line
CSS
CSSID$("but1",
"background-color:
red;")
Will give this
result :
Combining with the
style for the other button:
a$ = a$
+ cssid$("but1",
"background-color:
red;")
a$ = a$
+ cssid$("but2",
"background-color:
green;")
CSS
a$
A set of functions
is included to simplify the creation of HTML pages as we will see
later, so no need to worry if you are not familiar with writing
HTML code.
Now we will mention
an important ‘event’ that can be used to automatically fill the
content of the page each time a client connects to the module :
OnHtmlReload.
This ‘event’
defines a place where the program will jump to as soon as a
Websocket connection request is accepted.
Let’s clarify with
an example :
OnHtmlReload
Fill_Page
‘will jump to Fill_Page when the
page is reloaded
gosub
Fill_Page
'load the page for the first
time
Wait
‘pause waiting for the event
Fill_Page:
‘place where the page begins to
be created
CLS
a$ = "Now style me,
please<br>"
a$ = a$ +
"Button1: <button id='but1'
type='button'>ON</button> "
a$ = a$ +
"Button2: <button id='but2'
type='button'>OFF</button>"
HTML
a$
a$ = cssid$("but1",
"background-color:
red;")
a$ = a$ +
cssid$("but2",
"background-color:
green;")
HTML
a$
RETURN
|
The result will
be:
Now try to play
with the button "Reconnect"; you’ll see that, at each time the page
reconnects to the module, the HTML content is built and sent again.
This ensures that each time a client connects to the module it will
receive the correct content. At the same time, if other clients are
already connected, the content of all the pages will be refreshed
simultaneously. This ensures a synchronized content between all the
clients.
As said previously,
in order to simplify the creation of HTML pages there are several
functions available which can generate the html code
automatically.
Let’s start with
the button.
A button is an
object that is used to trigger an action each time it is pressed on
the web page.
The function is
BUTTON$.
Let’s explain with
an example:
CLS
HTML
BUTTON$("Button1", jump1)
Wait
'pause waiting for the
event
Jump1:
PRINT
"Clicked on Button1"
Return
|
The result will
be:
Try clicking on the
button then checking the result in the terminal console; the
message "Clicked on Button1" will be shown at each click.
To style the
button, we need to modify the syntax of the BUTTON$
command slightly; in fact we need to add another parameter to give
the button an ID:
CLS
HTML
BUTTON$("Button1", jump1, "but1") '
"but1" is the ID
Wait
'pause waiting for the
event
Jump1:
PRINT
"Clicked on Button1"
CSS
cssid$("but1",
"background-color:
red;") 'the same ID is used here
Return
|
Clicking on the
button now will change its color to red
Now we can now
introduce the LED object. The LED object is a circle that can be
filled in red or green depending on the content of a variable. The
function is LED$
As usual, let’s
start with an example:
CLS
led = 1 ‘this is the variable
associated with the LED. With 0 the led is red, with 1 the led is
green
HTML
LED$(led)
|
The result will
be:
Let’s also add a
button :
CLS
led
= 0
a$
= BUTTON$("Button1", jump1, "but1") '
"but1" is the ID
a$
= a$ + LED$(led)
HTML
a$
Wait
'pause waiting for the
event
Jump1:
PRINT
"Clicked on Button1"
led
= 1 -
led ' invert the
variable
REFRESH
' refresh (update) the variables between the code
and the html
Return
|
The result will
be:
Clicking on the
button will toggle the led between red and green colors.
The command
REFRESH
permits to update (synchronize) the variables in the code with the
corresponding objects variables on the web page. It should be run
each time a variable is modified.
As a simpler
alternative, the command AUTOREFRESH
will regularly sync the variables.
The command must be
run with the desired refresh timing.
Example
AutoRefresh
500 will refresh the variables each 500
milliseconds.
The interval
should not be less than 300 milliseconds (otherwise the module will
be too busy).
The example :
CLS
led = 0
a$ = BUTTON$("Button1", jump1, "but1") '
"but1" is the ID
a$ = a$ +
LED$(led)
HTML
a$
AutoRefresh
300
'sync each 300
milliseconds
Wait
'pause waiting for the
event
Jump1:
PRINT
"Clicked on Button1"
led = 1 - led
' invert the
variable
Return
|
The result will be
the same as the previous example.
Now it’s time to
introduce another object; the TEXTBOX with the corresponding
function TEXTBOX$.
The TEXTBOX will
display a ‘text box’ on the web page which is linked with a
variable. When the variable is modified in the code, the TEXTBOX
content will be updated on the web page, and vice-versa.
This lets us
introduce another ‘event’, the OnHtmlChange
command.
This ‘event’
defines a branch for the program to jump to whenever a variable is
modified inside the web page.
As usual, let’s
start with an example:
CLS
text$ = "Change me,
please"
HTML
TEXTBOX$(text$)
OnHtmlChange
Jump1
'will jump to Jump1 when a variable
changes on the web page
Wait
'pause waiting for the
event
Jump1:
Print
text$
'print the content of the variable
inside the terminal console
Return
|
Try now to change
the content of the textbox and press "Enter" on the keyboard.
Let’s see the
result in the terminal console:
With the concepts
already learned you’ll be able to use the other objects using the
similar logic.
Refer to the pages
below to understand the syntax of each object.
A timer is an "object" that permits the
execution of a particular action at regular intervals.
When the given time expires, the normal
execution of the program is interrupted while control is passed to
the "timer interrupt routine" until after the execution of the
return command.
Then the program continues from the point
where it was interrupted.
Let’s explain with an example :
timer0
1000,
mytimer
wait
mytimer:
wlog "mytimer " +
time$
return
|
Annex WI-Fi Basic
implements 2 timers, Timer0 and Timer1.
The Timer0 has a
higher priority against Timer1.
Many of the actions
are not executed directly by basic commands but can be executed as
asynchronous events.
An "event" is
simply an action that can be executed when something happens.
For example, pin
change interrupts are asynchronous events which can happen at any
time without user control.
In order to manage
the events, a list of commands "ONxxxx" is provided. These commands
define the place where the normal execution of the program will
branch to when the event occurs.
So, when the
"event" happens, the basic interpreter interrupts the normal
execution of the code and "jumps" to the location defined by the
corresponding command "ONxxx". As soon as the code associated with
the "event" is terminated with the command "return", the basic
interpreter continues from the previous interrupted location.
This is a special event that happens every
time aBUTTON$
object is clicked in the HTML pages.
When this happens,
a special variable
HtmlEventButton$ is created containing the name of the
button that was clicked.
This is useful to
determine the button within a group of buttons.
Let’s see an
example:
CLS
HTML
Button$("ON",
buttonEvent) +
" " + Button$("OFF",
buttonEvent)
wait
buttonEvent:
print
"You clicked
on ";
HtmlEventButton$
return
|
This event is triggered when an object present
in the HTML output page changes its value.
It is useful to make actions when something
changes in the HTML Pages.
When this event
happens, a special variable
HtmlEventVar$ is created containing the name of the
variable that changed its value.
This is useful to
determine the object that generated the event.
Let’s see an example :
CLS
text$ = "Change me,
please"
HTML
TEXTBOX$(text$)
OnHtmlChange
Jump1
'will jump to Jump1 when a variable
changes on the web page
Wait
'pause waiting for the
event
Jump1:
Print
text$
'print the content of the variable
inside the terminal console
Return
|
Note that the
special variable
HtmlEventVar$ is only created when the OnHtmlChange
event populates it due to a html object change, therefore it will
cause an error if tested for before an object is changed unless
specifically defined beforehand, eg: HtmlEventVar$ =
“”
This event is triggered when a Websocket
connection request is accepted.
This can be used to automatically fill the
content of the WEB page each time a client connects to the
module.
Let’s see an example :
CLS
OnHtmlReload
Fill_Page
'will jump to Fill_Page when the page
is reloaded
gosub
Fill_Page
'load the page for the first
time
Wait
'pause waiting for the
event
Fill_Page:
'place where the page begins to be
created
CLS
a$ = "Now style me,
please<br>"
a$ = a$ +
"Button1: <button id='but1'
type='button'>ON</button> "
a$ = a$ +
"Button2: <button id='but2'
type='button'>OFF</button>"
HTML
a$
a$ = cssid$("but1",
"background-color:
red;")
a$ = a$ +
cssid$("but2",
"background-color:
green;")
HTML
a$
Return
|
This event is
triggered when a code is received by the infrared receiver.
Refer to chapter
INFRARED
INTERFACE for more details.
This event is
triggered when a message is received on the serial port.
Example:
print
"Ram Available "; ramfree
onserial rec1
wait
rec1:
'print serial.input$
print serial.chr$;
return
|
This event is
triggered when a message is received on the serial port #2.
Example
serial2.mode 9600, 2, 5
' set serial port #2 to 9600 pin 2 TX,
pin 5 RX
print2 "Ram Available "; ramfree
onserial2 rec2
wait
rec2:
print serial2.input$
return
|
This event is
triggered when the TFT screen is touched.
Refer to the
chapter TouchScreen for more details.
This event is
triggered when a UDP message is received.
Example:
udp.begin
5001
'set the UDP commmunication using port
5001
onudp
goudp
'Write several messages to the port
for i
=
0 to 100
udp.write
"192.168.1.44",
5001,
"Hello "
+
str$(i)
next i
wait
goudp:
v$
=
udp.read$
'receive the UDP data
print
v$
return
|
This event is
triggered when a WgetAsync message is received.
This is associated
with the command
WGETASYNC.
The goal of the
WGETASYNC command is to start a html get request without
the module having to wait for the answer.
Because the
response is async, this command specifies the location where the
program should branch to when a message is received.
Example:
ONWGETASYNC
answer_done
WGETASYNC("www.fakeresponse.com/api/?sleep=5",
80)
For
i =
0 to
10000
' a
kind of sleep just to demonstrate that the code continue to
run
Print i
Next
i
Wait
answer_done:
Print
WGETRESULT$
Return
|
This event is
triggered as soon as a web client requests for a web page with the
url composed with http://local_ip/msg?param=value.
This kind of request is typically called an AJAX request as it
permits to exchange in both directions between the client (the web
browser) and the server (the ESP module).
In fact, in the url
request, the client can send parameters in the form of couples of
"param=value" separated by the character "&". For example, if
the client wants to send 2 parameters, it can send the following
request :
http://local_ip/msg?param1=value1¶m2=value2.
As soon as this
message is received by the ESP module, the event OnUrlMessage is
triggered; this means that the program will continue from the
location defined by the command OnUrlMessage.
As soon as the
message is received, the parameters sent by the client can be got
with the function UrlMsgGet$ and a message can be sent back to the
client with the command UrlMsgReturn.
Let’s see an
example :
onUrlMessage
urlAjax
wait
urlAjax:
wlog
"message received " + UrlMsgGet$("a")
+ "
" + UrlMsgGet$("b")
UrlMsgReturn
"Message sent back " + time$
print
UrlMsgGet$("b"),
ramfree
return
|
Now using another
web browser window, let’s type the following url :
http://esp_local_ip/msg?a=10&b=20
As you can see in
the following picture, the message is received by the ESP
module
At the same time,
the client receives the message sent back from the ESP module
If the program is
stopped, the module will answer with the message "STOPPED"
Now, let’s see a more complete example :
cls
' this is the
default value for pwm out
R
= 512
G
= 512
B
= 512
'Setup the
pwm channels
PWM.SETUP
12, 1, R,
10000, 10
PWM.SETUP
15, 1, G,
10000, 10
PWM.SETUP
13, 1, B,
10000, 10
'Set the
default values
PWM.OUT
1,
R
PWM.OUT
2,
G
PWM.OUT
3,
B
' these are
the sliders
a$
= ""
a$
= a$ + |R <input
type="range" id="dimmer_R" oninput="setPWM()" onclick="setPWM()"
min="0" max="1023" value="| & str$(R)
& |"/><br>|
a$
= a$ + |G <input
type="range" id="dimmer_G" oninput="setPWM()" onclick="setPWM()"
min="0" max="1023" value="| & str$(G)
& |"/><br>|
a$
= a$ + |B <input
type="range" id="dimmer_B" oninput="setPWM()" onclick="setPWM()"
min="0" max="1023" value="| & str$(B)
& |"/><br>|
a$
= a$ + |<input
type='text' id="txbox" value='---'>|
html
a$
'this is the
javascript "AJAX" code
fun$
= |function setPWM() {|
fun$
= fun$ & |r=_$("dimmer_R").value;|
fun$
= fun$ & |g=_$("dimmer_G").value;|
fun$
= fun$ & |b=_$("dimmer_B").value;|
fun$
= fun$ & |var xmlHttp =
new XMLHttpRequest();|
fun$
= fun$ & |xmlHttp.open("GET", "msg?r=" + r +"&g=" + g
+"&b=" + b, false);|
fun$
= fun$ & |xmlHttp.send(null);|
fun$
= fun$ & |r =
xmlHttp.responseText;|
fun$
= fun$ & |_$("txbox").value = r;|
fun$
= fun$ & |return
r;}|
' this is
where the javascript code is inserted into the html
jscript
fun$
'this is
where the prog will jump on slider change
onUrlMessage
message
wait
message:
print
UrlMsgGet$()
PWM.OUT
1,
val(UrlMsgGet$("r"))
PWM.OUT
2,
val(UrlMsgGet$("g"))
PWM.OUT
3,
val(UrlMsgGet$("b"))
UrlMsgReturn
UrlMsgGet$()
return
|
Open the input page in another window and run
the program
Using an external RGB led, you’ll be able to
directly control its color.
You’ll see how the exchanges can be fast using
AJAX exchanges. This program uses javascript embedded into the
code. The javascript works with the function XMLHttpRequest.
A good reference for this function is here
AJAX - Send a Request To a Server
This event is
triggered when a ESP-NOW message is received.
Example:
espnow.begin
' init the ESP-NOW
onEspNowMsg message
' set the place where jump in case of message
reception
wait
message:
print "Message
Received!"
return
|
This event is
triggered when a ESP-NOW transmission error occurs.
This happens, in
particular, when the receiver device has not received the
message.
espnow.begin
' init the ESP-NOW
espnow.add_peer "60:01:94:51:D0:7D"
' set the MAC address of the receiver
onEspNowError status
' set the place where jump in case of TX error
espnow.write "TX
message"
' send the message
wait
status:
print "TX
error on ";
espnow.error$
' print the error
return
|
This event is generated when a MQTT message is
received or an MQTT event happens
Example:
....
onmqtt
mqtt_msg
wait
' receive messages from the server
mqtt_msg:
print
"TOPIC : ";
mqtt.topic$
print
"MESSAGE: ";
mqtt.message$
return
|
This event is generated when a “metadata” is
decoded when playing mp3 or streaming a web radio.
At startup, the module will try to
connect to the router using any parameters specified in the page
“Config”.
If no parameters are specified in
the “Config” page, or the connection is unsuccessful, it will
default to AP (Access Point) mode with IP address 192.168.4.1 with
the SSID composed of ESP(+ mac address).
If the connection is successful,
the module will use the IP address defined in the “Config” page or,
if no IP address is specified, the IP will be given automatically
by the Router DHCP server.
After the module has connected to
the router it will try to reconnect automatically if the connection
is lost.
There are several commands /
functions available to manage the WIFI.
The first function is
WIFI.STATUS which permits to get the status of
the connection.
print
WIFI.STATUS ’ print 3 if connected, 6 if
disconnected
The first useful command is
WIFI.CONNECT
SSID$, password$ [,
BSSID$] [, IP$ , MASK$ [, GATEWAY$]]
This command allows you to connect
to any WIFI network (STA mode) overriding the parameters defined
into the’ “Config” page. This function is async so the connection
is done in background, while the program continues to
run.
Is then possible to check the
status of the connection using the function
WIFI.STATUS
Example :
WIFI.CONNECT
"HOMENET", "MyPassword"
print
"connecting"
While
WIFI.STATUS <>
3
Print "."
pause 500
wend
Using the optional
parameter
BSSID$,
will enable the connection to a specific WiFi access
point.
The BSSID represents the MAC
address of the WiFi access point (the router) and it is defined as
6 bytes in hex format separated by colon, i.e.
AA:BB:CC:12:34:56.
For stand alone configuration or
for ESP-NOW applications, there is another command that puts the
module in AP mode.
This command is
WIFI.APMODE
SSID$,
password$ [, channel]
[,
IP$ , MASK$]
The result is immediate and the
status can be checked using the function
WIFI.MODE (see below).
The channel is optional and is 1
by default.
IMPORTANT : the password must be at
least 9 characters
It is eventually possible to
control the output power of the module with the command
WIFI.POWER pow
WIFI.POWER 5
’ set the output power
at 5 dBm.
The module can also be put in WiFi sleep mode.
This mode permits to turn off the WiFi reducing the power
requirements of the module; this is very useful for battery
oriented applications or for applications where the WiFi is not
required.
To put the module in “modem-sleep”, the
command to execute is
WIFI.SLEEP.
The module will stay in that mode until the
execution of the command
WIFI.AWAKE.
After this command, the module will reconnect
automatically to the router (the command
WIFI.CONNECT is not required).
Another function available
is
WIFI.CHANNEL that shows the current Radio
Channel used by the WIFI.
Using the function
WIFI.RSSI is it possible to get the
intensity of the signal received (RSSI)
It is also possible to scan for
the WiFi networks accessible around the module.
This can be done using the
command
WIFI.SCAN and the function
WIFI.NETWORKS(network$).
Example :
WIFI.SCAN
While
WIFI.NETWORKS(A$)
<
0
Wend
Print
a$
The result will be :
Vodaphone, 00:50:56:C0:00:08,
-50, 5
Orange, 00:50:56:C0:32:07, -70,
5
Xxxx,
00:50:56:C0:86:CA,-78, 12
These information represent, in
the order :
SSID, BSSID(mac address),
RSSI(signal intensity), Channel Radio
The function
WIFI.MODE returns the current mode of the WIFI
connection as below:
VALUE
|
MEANING
|
0
|
The WIFI is in sleep
mode
|
1
|
The WIFI is in STATION
mode
|
2
|
The WIFI is in AP mode
|
3
|
The WIFI in AP+STA mode
|
The WIFI in AP+STA mode can
be obtained by configuring the module in AP mode and then using the
command
WIFI.CONNECT in the program.
Using a “fake” SSID / password
(example
WIFI.CONNECT
"A",
"" ) can be used to switch the WIFI
into the AP+STA mode. This can be useful for mixed ESP32 /
ESP8266 ESP-NOW operations.
Another Wifi related command
is
OPTION.MAC
mac$ that
permits to modify the MAC address of the module.
This is very important for the ESP
Now functionality.
Example :
OPTION.MAC
"AA:BB:CC:DD:EE:FF"
In addition, the functions
BAS.SSID$ and
BAS.PASSWORD$ returns respectively the login and
the password used for the STATION wifi connection.
If a program is defined to run automatically
(“Autorun File” in the config page), the WiFi connection process is
slightly different.
If the option “Fast boot” in the config page
is selected, the program will be executed immediately and the WiFi
will be powered ON after a little delay ( 0.1 sec ).
If the command
WIFI.SLEEP is executed during the very beginning of the
program ( for example as the first line of the program) the WiFi
will be simply disabled without using any power.
This enhances the use of the module in low
power applications (i.e. on battery).
The WiFi connection can then be restored later
using the commands
WIFI.CONNECT or
WIFI.APMODE.
If the command
WIFI.SLEEP is not executed at the beginning of the
program, the WiFi connection will be established by default as
described in the previous chapter (WiFI CONNECTIONS).
The function
BAS.RESETREASON can be used at the beginning of the
program to understand the reasons for the restart of the module
enabling it to take the appropriate actions.
In addition, the function
BAS.WAKEUPREASON can be used to determine the cause of
the wakeup from sleep if the module was in deep sleep mode.
In case of any IP or Autorun problem
preventing the module from being accessed, it is possible to
temporarily bypass the IP settings of the module and disable the
Autorun file by connecting the serial TX and RX pins together
(GPIO1 to GPIO3) during the startup phase (power up).
This could happen if, for example, a wrong IP
address has been set.
Doing this action when restarting the module
will put it in AP mode with the IP address at 192.168.4.1, just
like a module that has not been configured.
A message “Recovery Mode” will be printed on
the console, but none of the existing files on the module will be
modified, including the internal configuration parameters.
In this mode it will be possible to gain
access to the module for changing such correct wrong IP parameters
using the configuration page.
When the TX/RX link is removed, the module can
be rebooted to the configured settings at the next restart.
The module can be put into low energy mode to
minimise as much as possible the power requirements.
This mode is called deep sleep and
should reduce the power consumption to a few µA but this is a
function of each ESP32 module as the power requirement
includes the different components installed on the module.
When the module is put into deep sleep all the
module activities are stopped, all the memory content of the module
is lost except for the RTC memory (this is a special memory block
inside the module that holds its content even if the module is
reset, but not when the module is powered OFF).
At the end of the sleep period, the
module restarts and reloads the program defined as autorun from the
beginning (from the first line).
To put the module in deep sleep the following
command is available :
SLEEP
value [, pin, level]
This command puts the ESP32 in deep sleep (low
energy) for 'value' seconds.
At the end of the period, the unit will reboot
and reload the default basic program.
Example
' Sleeps for 600 seconds (10 minutes)
The period can go from 1 second to
several years (1 year = 31,536,000 seconds)
Optionally, it is possible to wake up the
module using an external signal sent on an input pin
In this case the pin and the level must be
specified in addition to the time value.
Example
' Sleeps for 3600 seconds (1 Hour) or until the pin 32 goes to
high
SLEEP
3600, 32, 1
Only RTC IO can be used as a source for
external wake up.
They are pins: 0,2,4,12-15,25-27,32-39.
Level is 1 for wakeup on High and 0 for wakeup
on Low
Optionally, this command can be also used to
wake up the module using the capacitive touch on an input pin.
In this case the command SLEEP maintains the
same syntax but level defines the threshold value for the pin.
Only the following pins can be used for that
purpose (capacitive touch) : 0, 2, 4, 12, 13, 14, 15, 27 32, 33
Level must be > 1 to enable the touch (0
and 1 are reserved for the wake up on Low or Wake up on High).
Example
' Sleeps for 3600 seconds (1 Hour) or until the pin 15 is
touched
SLEEP
3600, 15, 40
' Threshold at 40
The RTC memory will survive after the wake up
permitting to take trace of the actions done before the sleep.
This memory can be set as below :
BAS.RTCMEM$ =
"data to be saved during deep sleep"
And can be read as below :
A$
=
BAS.RTCMEM$
Note : the RTC memory can hold up to 7680
bytes
The ESP module normally synchronises its date
and time from either of two NTP time servers ("pool.ntp.org" and
"time.nist.gov"). Optionally an alternative (eg: intranet) time
server can be defined using the [CONFIG] page.
Using these servers the ESP doesn’t require
any date/time setting (except the configuration of the Time Zone
and DST done using the [CONFIG] page).
The timezone is defined as a string
likeCET-1CEST,M3.5.0,M10.5.0/3
that describes how the local time must be managed in terms of time
shift and DST (summer / winter time).
A complete list of timezone strings can be
found here :
https://github.com/nayarsystems/posix_tz_db/blob/master/zones.csv
An internal timekeeper has been included if no
time server is available, e.g. no available internet access.
This timekeeper starts from 01/01/1970
00:00:00 and counts the seconds since the power on of the
module.
If internet connection becomes available
later, the internal timekeeper will sync its time with the NTP
servers.
The time can be sync with the NTP time server
at any moment using the command
OPTION.NTPSYNC.
This time and date can be manually set using
the command SETTIME.
The Syntax is :
SETTIME year, month, day, hours, minutes,
seconds
Example
Set the date to 02 September 2017 at
13:58:12
SETTIME 17, 9, 2,
13, 58, 12
The time and date can also be
manually synchronised to the computer using the "Time Sync" button
in the File Manager window of the computer utility ‘tool’ if it has
a websocket connection.
WARNING:
In both cases of manual
setting, the time and date will default back to 1970 defaults at
the next module restart, so will require setting
again.
For more information about the
Time Zones and DST, please consult the following page
:
Time Zone and
DST
It is also possible to connect
an RTC (DS1307 or DS3231) to the module.
See the chapter
“RTC
Module”
for more details.
The following functions use the
“Unix Time Stamp” format :
DATEUNIX(date$),
TIMEUNIX(time$),
UNIXDATE$(value [,format]),
UNIXTIME$(value)
The “Unix Time Stamp” is a way
to track time as a running total of seconds.
This count starts at the Unix
Epoch on January 1st, 1970 at UTC.
Therefore, the unix time is
merely the number of seconds between a particular date and the Unix
Epoch.
In synthesys :
-
DATEUNIX("01/01/18")
returns the number
of seconds from 01/01/1970 to the specified date 01/01/2018
(1514764800)
-
TIMEUNIX("12:30:55")
returns the number
of second since midnight (45055)
-
UNIXDATE$("1532773308")
returns
28/07/18
-
UNIXTIME$(1532773308)
returns
10:21:48
Annex32 includes a
FATFS file system hosted on the flash memory chip.
It “emulates” a
disk file system enabling it to save and load files in a
transparent way.
Depending on the
size of the flash chip, the following free space is available :
Flash Chip size
|
Free space available
|
4M
|
1MB
|
[6] 8M
|
5MB
|
16M
|
13MB
|
Annex32 can also
use an SD CARD connected as described in the chapter SD CARD
ADAPTER.
Both the internal
FATFS and the SD CARD utilise the FAT32 file system
This means that
there are no particular limitations in terms of filename length and
directories, compared to the SPIFFS file system limitations hosted
in the ESP8266.
Unlike normal
variables, filenames and folders are case sensitive.
Annex32 supports SD
CARDS up to 16GB.
The internal and the external (SDcard)
space are mutually exclusive and cannot be accessed at the same
time.
By default Annex32 will use the SD, if
available, otherwise it will use the internal flash disk space
(FATFS).
Both the
internal FATFS and external SD CARD share the same command and
functions.
All the file
related functions share the same prefix FILE.
followed by the specific function.
FUNCTIONS / COMMANDS
|
DESCRIPTION
|
FILE.COPY(filename$,
newfile$)
|
Copy the file filename$
into the file newfile$
Returns 1 in case of success or
0 if error
|
|
Delete the file specified by filename$
Returns 1 in case of success or
0 if error
|
|
Returns 1 if filename$
exists, otherwise returns 0
|
|
Rename the file oldname$
to newname$
Returns 1 in case of success or
0 if error
|
|
Returns the size of the file (in bytes) if the
file exist, otherwise returns -1
|
|
Create a directory specified by
dirname$
Returns 1 in case of success or
0 if error
|
|
Remote the directory specified by
dirname$
Returns 1 in case of success or
0 if error
|
|
Will search for files and return the names of
entries found.
path$
represents the directory name.
path$ can
include wildcards characters as ‘*’,
‘.’ and
‘?’
The function will return the first entry
found.
To retrieve subsequent entries use the
function with no arguments. ie,
FILE.DIR$.
The return of an empty string indicates that
there are no more entries to retrieve.
|
FILE.READ$(filename$,
[line_num] | [start, length])
|
Returns the content of the file
filename$.
Specifying line_num,
only the corresponding line is read from the file.
If start
and length
options are specified, the file is read from the start
position for length
characters, otherwise the complete file is read in one go
The line number
starts from 1.
If the line is not
existing (reached the end of file), the function will return
“_EOF_” to indicate the end of the file.
|
|
Append the content of content$
to the file filename$.
If the file does not exist, it will be
created.
The file can be read back using the function
FILE.READ$(filename$)
File size is only limited by available disk
space (internal FFAT or external SD card)
|
FILE.SAVE
filename$,
content$
|
Save the content of content$
to the file filename$.
The file can be read back using the function
FILE.READ$(filename$)
File size is only limited by available disk
space (internal FFAT or external SD card)
|
|
Same functionalities as the previous
command.
Implemented for homogeneity with other
commands
|
|
Convert the file defined ‘source$’ into the
file defined in ‘dest$’.
The source file can be in any format but must
be encoded in base64 format. Useful for wokwi to store any file in
text format
|
|
Convert the file defined ‘source$’ into the
file defined in ‘dest$’.
The source file can be in any format and will
be encoded in base64 format.
|
|
See the chapter I/O buffer for more
details
|
[7] FILE.WRITE_IOBUFF
|
See the chapter I/O buffer for more
details
|
[8] FILE.APPEND_IOBUFF
|
See the chapter I/O buffer for more
details
|
|
See the chapter I/O buffer for more
details
|
Examples:
List all the files
in the directory /html
d$
= FILE.DIR$("/html")
While D$
<> ""
wlog d$
d$
= FILE.DIR$
Wend
|
File operations
file.save
"/test.bas",
"The quick brown fox "
wlog
"exists",
file.exists("/test.bas")
wlog
"size",
file.size("/test.bas")
file.append
"/test.bas",
"jumps over the lazy dog"
wlog
"size",
file.size("/test.bas")
wlog
"copy",
file.copy("/test.bas",
"/AAA.bas")
wlog
"size",
file.size("/AAA.bas")
wlog
"rename",
file.rename("/AAA.bas",
"/BBB.bas")
wlog
"size",
file.size("/BBB.bas")
wlog
"size",
file.size("/AAA.bas")
wlog
"read",
file.read$("/test.bas")
wlog
"delete",
file.delete("/BBB.bas")
|
Download files from another module or WEB
server
The command
FILE.DOWNLOAD
url$,
file_path$
retrieves a file from a specified URL (url$) and saves it
to the local file path (file_path$). This
can be used both as a standalone command or as a function that
returns a status code, indicating the success or failure of the
download operation.
●
url$: A string specifying the URL of the file to download.
This should be a valid HTTP or HTTPS URL.
●
file_path$: A string specifying the local path where the
downloaded file should be saved.
When used as a function,FILE.DOWNLOAD
returns an integer value that indicates the status of the
download:
●
1: Download completed successfully.
●
-1: Not enough space available to download the file.
●
-2: Failed to open the file for writing.
●
-3: Failed to create an HTTP client.
●
Other HTTP error codes: Various HTTP error codes that may be
returned by the server (e.g., 404 for "Not Found", 500 for
"Internal Server Error").
This command is
also useful for downloading (copying) a file from another Annex RDS
module. By providing the appropriate URL, you can easily transfer
files between modules, which is especially useful for distributing
updates or configurations across multiple devices.
Notes:
● The
function handles both HTTP and HTTPS URLs, using a secure
client
● The
function checks for available space before attempting the download
and will abort if there is insufficient space.
● All
operations are logged to the serial output for debugging
purposes.
Example 1:
download from the WEB
FILE.DOWNLOAD
"https://updates.cicciocb.com/annex-logo.png",
"/logo.png"
or
Wlog
FILE.DOWNLOAD("http://updates.cicciocb.com/annex_bee_new.png",
"/logo2.png")
Example 1:
download from another Annex RDS Module with IP: 192.168.1.181
FILE.DOWNLOAD
"http://192.168.1.181/program1.bas",
"/program1.bas"
or
Wlog
FILE.DOWNLOAD("http://192.168.1.181/program1.bas",
"/program1.bas")
print file.download
("https://updates.cicciocb.com/annex-logo.png", "/logo.png")
The I/O BUFFER is a functionality that gives
the capability to hold and manage binary data.
In short, the I/O buffer is a block of RAM
memory that can be exchanged as a block or read and written byte
per byte. It overcomes the limitation of strings, which are
unable to include the character ASCII 0 (NUL).
It has a defined length and can be freely
dimensioned and cleared.
It can be used in the code using the
IOBUFF keyword, and Annex exposes 5 I/O buffers numbered
from 0 to 4.
The I/O buffers can have any size within the
limits of the free RAM memory available.
The main goal of this functionality is to
interface with all the functions that require exchanges using
binary data.
In the current implementation it can be used
with :
-
Files
-
Serial Ports
-
SPI
-
I2C
-
UDP
As it is essentially a block of memory,
the first command is
IOBUFF.DIM(buff_num,
size) that defines its
size.
buff_num
can span from 0 (first buffer) to 4 (last buffer)
size
can span from 0 to the maximum RAM memory available
Example:
IOBUFF.DIM(0,
1000) 'dimension the I/O buffer 0 with 1000
bytes
The I/O buffer can be filled with a given set
of data directly using the function
IOBUFF.DIM
Example:
IOBUFF.DIM(0,
10) = 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
IOBUFF.DIM(1,
5) =
&h12, &hAA, &h50, &O377,
&B10101010
As soon as the buffer is dimensioned, the
given amount of RAM is reserved for the buffer.
When not required anymore, it can be removed
with the command
IOBUFF.DESTROY(buff_num)
Example:
IOBUFF.DESTROY(0)
'remove the buffer releasing the memory
reserved
Note : The I/O buffers are automatically
removed each time the program is run.
It is possible to know the size of the buffer
using the function
IOBUFF.LEN(buff_num)
Example
Print
IOBUFF.LEN(buff_num)
'print the length of the
buffer
The I/O buffer can be read byte per byte using
the function
IOBUFF.READ(buff_num,
position)
position
can span from 0 (first byte) and the buffer length - 1 (last
byte)
Example:
Print
IOBUFF.READ(0,
4) 'print the byte 4 from the I/O buffer
0
A
= IOBUFF.READ(0, 7)
' read in the variable A the byte 7
from the I/O buffer 0
The I/O buffer can be written byte per byte
using the command
IOBUFF.WRITE(buff_num,
position, value)
position
can span from 0 (first byte) and the length - 1 (last byte)
value
can span from 0 to 255 (byte)
The I/O buffers communicate with the other
modules using the following syntax:
-
xxxx.READ_IOBUFF(buff_num)
Receive data in the
buffer buff_num
-
xxxx.WRITE_IOBUFF(buff_num,
start, size)
Transmit (send)
data from the buffer buff_num
starting from the position ‘start’
for ‘size’
bytes
-
xxxx.REPLY_IOBUFF(buff_num,
start, size)
Reply to the sender
data from the buffer buff_num
starting from the position ‘start’
for ‘size’
bytes
Where
xxxx can be :
UDP
SERIAL
SERIAL2
FILE
I2C
SPI
Detailed syntax :
UDP.READ_IOBUFF(buff_num)
SERIAL.READ_IOBUFF(buff_num)
SERIAL2.READ_IOBUFF(buff_num)
FILE.READ_IOBUFF(buff_num),
filename$ [, position, nb_of_bytes_to_read]
I2C.READ_IOBUFF(buff_num),
address, register, nb_of_bytes_to_read
SPI.READ_IOBUFF(buff_num),
nb_of_bytes_to_read
UDP.WRITE_IOBUFF(buff_num
[, start [, size]]), IP$,
port
SERIAL.WRITE_IOBUFF(buff_num
[, start [, size]])
SERIAL2.WRITE_IOBUFF(buff_num
[, start [, size]])
FILE.SAVE_IOBUFF(buff_num
[, start [, size]]),
filename$
FILE.WRITE_IOBUFF(buff_num
[, start [, size]]),
filename$
FILE.APPEND_IOBUFF(buff_num
[, start [, size]]),
filename$
I2C.WRITE_IOBUFF(buff_num
[, start [, size]]), address,
register
SPI.WRITE_IOBUFF(buff_num
[, start [, size]])
UDP.REPLY_IOBUFF(buff_num
[, start [, size]])
[,port]
SPI.REPLY_IOBUFF(buff_num
[, start [, size]]), (buff_num_reception)
The IOBUFFER can be used for sending or
receiving data.
When used for receiving data, the syntax is
always.READ_IOBUFF(buff_num).
When receiving data, it is not necessary to
dimension the buffer before as it will be automatically dimensioned
depending on the size of the data received. If the buffer was
already containing some data, these will be flushed and replaced by
the new data.
For example, the following command receives
all the data available from the serial port 2 in the buffer 3 :
SERIAL2.READ_IOBUFF(3)
This command receives the data coming from an
UDP connection in the buffer 1:
UDP.READ_IOBUFF(1)
Additionally some other arguments may be
required.
This command reads 512 bytes from the file
data.bin starting from the file position 123 in the buffer 0:
FILE.READ_IOBUFF(0),
“/data.bin”, 123, 512
This command reads 8 bytes from an I2C device
with address 63 from the register 19 in the buffer 4 :
I2C.READ_IOBUFF(4),
63, 19, 8
This command reads 32 bytes from the SPI bus
in the buffer 2 :
SPI.READ_IOBUFF(2),
32
When used for sending data, the syntax is
always
.WRITE_IOBUFF(buff_num
[, start [, size]])
When sending data, it is possible to send the
entire buffer or only a part of it.
Specifying the optional arguments start and
size it is possible to define the part of the buffer to be sent;
otherwise, if omitted, the entire buffer will be transferred.
For example, the following command sends 10
bytes from the buffer 1 starting from the position 45 to the serial
port :
SERIAL.WRITE_IOBUFF(1,
45, 10)
This command sends the complete buffer 1 to
the serial port 2
SERIAL2.WRITE_IOBUFF(1)
This command sends 8 bytes from the buffer 2
starting from the position 128 to the SPI bus
SPI.WRITE_IOBUFF(2,
128, 8)
Additionally some other arguments may be
required.
This command sends 12 bytes from the buffer 1
starting from the position 64 to the UDP on the address
192.168.1.89 and port 8080 :
UDP.WRITE_IOBUFF(2,
128, 8), “192.168.1.89”,
8080
This command sends the entire buffer 2 on the
same UDP device :
UDP.WRITE_IOBUFF(2),
“192.168.1.89”, 8080
This command writes the buffer 1 to the file
data.bin
FILE.WRITE_IOBUFF(1),
“data.bin”
This command has the same result and is
provided for compatibility with the existing syntax
FILE.SAVE_IOBUFF(1),
“data.bin”
This command appends 36 bytes from the buffer
0 starting from the position 25 to data.bin
FILE.APPEND_IOBUFF(0,
25, 36), “data.bin”
This command sends the buffer 2 to the I2C
device with address 63 and register 19 :
I2C.WRITE_IOBUFF(2),
63, 19
The same operation but sending only 4 bytes
starting from position 0:
I2C.WRITE_IOBUFF(2,
0, 4), 63, 19
The syntax
.REPLY_IOBUFF(buff_num
[, start [, size]])
defines some kind of special operations.
For example, this command sends the buffer 0
back to the UDP message sender:
UDP.REPLY_IOBUFF(0)
This is the equivalent of
UDP.REPLY message$
but with the IOBUFFER
Optionally it is also possible specify part of
the buffer and the destination port (eg: 5001) as below:
UDP.REPLY_IOBUFF(0,
2, 6), 5001
When used with the SPI bus, it transmits and
receives at the same time.
As this operation requires 2 buffers, both
must be specified.
For example, this command sends the buffer 0
and receive into the buffer 2:
SPI.REPLY_IOBUFF(0),
(2)
This command sends 4 bytes from the buffer 0
starting from the position 89 and receive 4 bytes in the buffer
3:
SPI.REPLY_IOBUFF(0,
89, 4), (3)
Several other functions / commands are
available for advanced users.
These enable bit, string and hex
operations
conversion from hex
string :
IObuff.FromHex(buff_num,
var$ [, pos])
var$ must
contain a hex string in the form of "aabbcc1235"
pos is the
position where the HEX must be included in the buffer (0 by
default)
A part of the string
can be converted in combination with mid$
conversion from
string:
IObuff.FromString(buff_num,
var$ [, pos])
var$ must
contain a text string in the form of "This is a text string"
pos is the
position where the HEX must be included in the buffer (0 by
default)
A part of the string
can be converted in combination with mid$
conversion to hex
string:
A$
= IObuff.ToHex$(buff_num, [, start [,
size]])
returns an hex string
in the form of "aabbcc1235"
start and
size are optional and define the start and length (like MID$
but the 1st byte is 0)
conversion to
string:
A$
= IObuff.ToString$(buff_num, [, start [,
size]])
returns a text string
in the form of "This is a string"
start and
size are optional and define the start and length (like MID$
but the 1st byte is 0)
a
= IObuff.bit(buff_num, position, bit)
returns the value of
the bit of the byte at the position of the
buff_num
returns 0 or 1
IObuff.setbit(buff_num,
position, bit)
set the bit of
the byte at the position of the buff_num
IObuff.clearbit(buff_num,
position, bit)
clear the bit
of the byte at the position of the buff_num
IObuff.togglebit(buff_num,
position, bit)
toggle the bit
of the byte at the position of the buff_num
IObuff.copy(dest_buff_num
[,pos]) , (source_buff_num, [,
start [, size]])
Copy the from
source_buff to dest_buff
pos is the
position where the source must be copied in the source (0 by
default)
start and
size define what must be copied (have the same meaning as in
.WRITE_IOBUFF)
UDP - use the remote controller APP for IOS
devices (iphone and Ipad)
' I/O buffers example using the RCWController
' available in the IOS app store.
' It uses by default the port 10000
' The APP sends a block of 10 bytes that
' will be printed in the console on the same line
udp.begin 10000
' define the place where jump on message reception
onudp received
wait
received:
' read the incoming data in the buffer 0
udp.read_iobuff(0)
size
= iobuff.len(0)
print
"received "; size; "
bytes"
for z
= 0 to 9
' read and print 1 byte at the time on
the same line
print iobuff.read(0, z),
next z
Print
' print an empty line
return
|
File read and transfer to the serial port
by blocks
' I/O BUFFERS example using files
' read a file in blocks of 512 characters
' and send them to the serial port (print)
fileName$
= "/data8.txt"
block_size
= 512 '
size of the block to be read
file_size
= file.size(fileName$)
print
"File size "; file_size
print file_size
for z
= 0 to file_size - 1 step
block_size
file.read_iobuff(0), fileName$, z, block_size
' send the block on the serial port
(print)
serial.write_iobuff(0)
next
|
Serial port data logger
' I/O BUFFERS example to create a serial data logger
' receive bytes from the serial port and
' write them into the file /mylog.txt
' all the characters will be recorded
' including the ASCII 0 (NUL)
filename$
= "/mylog.txt"
' define the place where jump on message reception
onserial received
wait
received:
' waits for 10 millisec giving time to receive all the
data
pause 10
' read the incoming data in the buffer 0
serial.read_iobuff(0)
size
= iobuff.len(0)
print
"received "; size; "
bytes"
' appends the received data to the file
file.append_iobuff(0),
filename$
return
|
This diagram
shows pin mapping for the popular ESP32 DEV Board
module.
(*) pins GPIO6 to
GPIO11 are not available.
Annex 32, as it
supports by default the M5stack wiring, assumes the following pins
already allocated/dedicated
PIN
|
FUNCTION
|
DESCRIPTION
|
32
|
PWM
BL TFT
|
Backlight TFT display
|
33
|
RST
TFT
|
RST
pin TFT
|
27
|
D/C
TFT
|
D/C
pin TFT
|
14
|
CS
TFT
|
CS
pin TFT
|
23
|
SPI
MOSI
|
SPI
MOSI pin (shared with SD and TFT)
|
19
|
SPI
MISO
|
SPI
MISO pin (shared with SD and TFT)
|
18
|
SPI
SCK
|
SPI
CLOCK pin (shared with SD and TFT)
|
4
|
CS
SDCARD
|
CS
pin SDCARD
|
0
|
CS
TFT TOUCH
|
CS
pin Touchscreen (from the TFT)
|
|
|
|
3
|
RX0
|
Serial Port RX pin
|
1
|
TX0
|
Serial Port TX pin
|
|
|
|
25
|
SPEAKER
|
Speaker or mono audio output
|
21
|
SDA
I2C
|
I2C
SDA pin
|
22
|
SCL
I2C
|
I2C
SCL pin
|
|
|
|
2
|
I2S
DATA
|
Audio
DAC I2S DATA pin
|
5
|
I2S
BCLK
|
Audio
DAC I2S BCLK pin
|
26
|
I2S
LRCK
|
Audio
DAC I2S LRCK pin
|
16
|
PSRAM
|
Optional PSRAM
|
17
|
PSRAM
|
Optional PSRAM
|
Pin numbers
correspond directly to the ESP32 GPIO pin numbering.
The function of the
pin (input / output) must be defined before using the function
PIN.MODE as below :
To define the pin 5
as input :
PIN.MODE 15,
INPUT
To define the pin 4
as input with a pullup:
PIN.MODE 4,
INPUT, PULLUP
To define the pin 4
as input with a pulldown:
PIN.MODE 4,
INPUT, PULLDOWN
To define the pin 2
as output
PIN.MODE 2,
OUTPUT
To define the pin 2
as output open collector
PIN.MODE 2,
OUTPUT, 1
Pins may also serve
other functions, like Serial, I2C, SPI.
These functions are
normally activated by the corresponding library.
The value from a
pîn can be read as shown below :
A
= PIN(5)‘ read from GPIO5 pin
The pin value can
be set as below
PIN(2)=
0
‘
set 0 on the GPIO2
pin
The pin value (0 or
1) can also be easily toggled by subtracting it from 1 (because
1-0=1 and 1-1=0), eg:
PIN(2)=
1 -
PIN(2)‘
toggles the value of GPIO2
pin
This part is applicable only
to the “classic” ESP32 as the other members of the family have
different H/W characteristics and
Although pin numbers can be from 0 to 39, gpio pins
6 to 11 should not be used because they are connected to
flash memory chips on most modules. Trying to use these pins as IOs
will likely cause the program to crash.
Pins 34 to 39 are
INPUT only and cannot be configured as
PULLUP or
PULLDOWN.
Pins 0 to 33 can be
INPUT,
OUTPUT,
INPUT, PULLUP or
INPUT, PULLDOWN.
Note:
If the module is equipped with PSRAM, the gpio pins
16 and 17 are reserved and must not be used.
The command
PIN.STRENGTH can be used to define the drive capability
of the output pins from a scale from 0 to 3.
For example, the
following command set the pin 15 with a drive capability of 2.
The ESP32 provides four drive strength levels for its GPIO
pins:
●
0: Weakest drive strength, approximately 5 mA.
●
1: Low drive strength, approximately 10 mA.
●
2: Medium drive strength, approximately 20 mA.
●
3: Maximum drive strength, approximately 40 mA.
Annex
supports two commands for serially shifting data:
-
PIN.SHIFTOUT for sending data
-
PIN.SHIFTIN for receiving data.
Both commands
allow control over bit order, bit count, and timing delays.
These commands are
in particular useful to control external shift registers or simple
SPI bus devices.
The
PIN.SHIFTOUT command is used to send data out serially
through a specified data pin. It shifts out data bits one at a
time, synchronised with a clock signal. The command allows you to
specify the order in which bits are shifted (least significant bit
first or most significant bit first), the number of bits to shift,
and the timing delay between each bit. This command drives the data
pin high or low according to the current bit in the data, and
toggles the clock pin to indicate when each bit should be read by
the receiving device.
Syntax:
PIN.SHIFTOUT
pin_data,
pin_clk, data [, bit_order] [, nb_bits] [,
delay_us]
Parameters:
●
pin_data:
○
Description: GPIO pin number used for the data line.
●
pin_clk:
○
Description: GPIO pin number used for the clock line that
synchronises the data transfer.
●
data
:
○
Description: Data to be shifted out.
●
Bit_order
(optional):
○
Description: Specifies the bit order for shifting data.
Possible values:
■
0
(default) : LSBFIRST Least Significant Bit first.
■
1
: MSBFIRST Most Significant Bit first.
●
Nb_bits
(optional):
○
Description: Number of bits to shift out from the data.
Default is 8 bits.
●
Delay_us
(optional):
○
Description: Delay in microseconds between each bit shift.
Default is 1 microsecond.
Details:
● This
command uses critical section management to ensure atomic
operations during the data shift process. However, because it
relies on software delays, the timing may not be very precise,
particularly for delays shorter than 10 microseconds
●
Bit Order: The bit order determines whether the least
significant bit (LSBFIRST) or the most significant bit (MSBFIRST)
is shifted out first.
●
Number of Bits: Specifies how many bits from the data value
will be shifted out. If not specified, the default is 8 bits.
●
Delay: Introduces a delay between the setting of the data
pin and toggling of the clock pin to control the timing of the data
shift.
Example:
PIN.SHIFTOUT 13, 14,
0xFF ' Shift out 8 bits
from data 0xFF
PIN.SHIFTOUT 13, 14,
0xFF, 1' Shift out 8 bits from data
0xFF with MSBFIRST
PIN.SHIFTOUT 13, 14,
0xFFFF, 1, 16, 2 ' Shift out 16
bits from data 0xFFFF with MSBFIRST, 2 µs
delay
|
The
PIN.SHIFTIN function is used to receive data serially
through a specified data pin. It shifts in data bits one at a time,
using a clock signal to determine when to sample the data pin.
Similar to
PIN.SHIFTOUT, it allows you to specify the bit order,
the number of bits to shift, and the timing delay between each bit.
This function reads the state of the data pin in sync with
the clock signal, capturing each bit as it arrives and storing it
in a variable.
Syntax:
PIN.SHIFTIN(
pin_data,
pin_clk [, bit_order] [, nb_bits] [, delay_us] )
Parameters:
●
pin_data:
○
Description: GPIO pin number used for the data line.
●
pin_clk
:
○
Description: GPIO pin number used for the clock line that
synchronises the data reading.
●
Bit_order
(optional):
○
Description: Specifies the bit order for shifting data.
Possible values:
■
0
(default) : LSBFIRST Least Significant Bit first.
■
1
: MSBFIRST Most Significant Bit first.
●
Nb_bits
(optional):
○
Description: Number of bits to read from the data line.
Default is 8 bits.
●
Delay_us
(optional):
○
Description: Delay in microseconds between each bit read.
Default is 1 microsecond.
Details:
● This
command uses critical section management to ensure atomic
operations during the data shift process. However, because it
relies on software delays, the timing may not be very precise,
particularly for delays shorter than 10 microseconds
●
Bit Order: Determines whether the least significant bit
(LSBFIRST) or the most significant bit (MSBFIRST) is read
first.
●
Number of Bits: Specifies how many bits will be read from
the data line. If not specified, the default is 8 bits.
●
Delay: Introduces a delay between each bit read, allowing
for control over timing and synchronisation.
Example:
A
= PIN.SHIFTIN(21, 22)
' Shift in 8 bits from data pin 21 with
clock pin 22
B
= PIN.SHIFTIN(13, 14, 1) ' Shift in 8
bits from data pin 13 with clock pin 14, reading
MSBFIRST
C
= PIN.SHIFTIN(13, 14, 1, 16, 2) ' Shift
in 16 bits from data pin 13 with clock pin 14, reading MSBFIRST, 2
µs delay
|
The
INTERRUPT command permits to trigger an event when the
signal on an input pin changes.
The interrupt is
triggered BOTH when the signal goes from LOW to HIGH and HIGH to
LOW.
Therefore a
momentary pulse actually generates 2 interrupts which need testing
for Hi or Lo as appropriate.
Example:
pin.mode 12,
input ' set pin 12 as input
interrupt 12, change_input
' set interrupt on pin 12
wait
change_input:
if pin(12)
= 0 then return ' if the pin is low, returns back
print "The pin changed to HIGH"
Return
|
It is possible to
add an optional parameter, mode,
that specify if the interrupt must be generated on the rising edge,
the falling edge or on change:
Syntax:
INTERRUPT pin_no,
{OFF | label} [, mode]
Parameters:
- pin_no: The input
pin number for which the interrupt is being defined. It must be an
integer value ranging from 0 to the maximum pin number supported by
the specific variant of the ESP32 (-s2, -c3, -s3).
- label: The branch
label to which the program will jump when the designated input pin
signal changes. It must be a valid label in the program's
context.
- OFF: Use this
keyword to remove the interrupt associated with the specified input
pin.
- mode (optional):
An integer parameter or keyword specifying the trigger condition
for the interrupt. It can take one of the following values:
- 1, RISING:
Rising edge trigger
- 2,
FALLING: Falling edge trigger
- 3, CHANGE:
Any change in signal trigger (default if 'mode' parameter is
omitted)
Examples:
-
To set up an interrupt that jumps to the 'PIN5_CHANGE' label when a
rising edge is detected on pin 5:
INTERRUPT 5,
PIN5_CHANGE, RISING
-
To remove the interrupt associated with pin 5:
INTERRUPT 5,
OFF
-
To set up an interrupt that jumps to the 'PIN13_TRANSITION' label
when any change is detected on pin 13:
INTERRUPT 13,
PIN13_TRANSITION , CHANGE
-
To set up an interrupt without specifying the trigger mode
(defaulting to any change in signal) that jumps to the
'PIN7_UPDATE' label when pin 7 signal changes:
INTERRUPT 7,
PIN7_UPDATE
Note:
-
Interrupts can provide a way to respond to external events
asynchronously.
-
The optional 'mode' parameter allows you to customize the trigger
condition for the interrupt to match your specific application
requirements.
The
input pin (pin_no) must be previously configured as an input before
setting up the interrupt.
Ensure
that the specified 'pin_no' and 'label' are valid within the scope
of your program.
The
maximum pin number supported by the ESP32 variant should be
considered based on the specific model (-s2, -c3, -s3).
Annex32 has 8 ADC
pins with 12 bits resolution which are available to users.
The function
ADC(pin)
can be used to read voltage on the pins defined in the table
below.
GPIO Pins Available as
Analog Input
|
32
|
33
|
34
|
35
|
36
|
37
|
38
|
39
|
To read the voltage applied at the pin, the
function ADC can be used as below :
print
ADC(39)' read voltage
from the pin 39
The voltage range is
0 ... 3.3V and the corresponding range returned by the function is
0 … 4095.
NOTE: When using the
function
ADC,the pin is automatically configured as an Analog
Input
Annex32
supports an additional ESP32 feature, the capacitive
touch.
With this
feature, it is possible to activate inputs with your fingers with
just one wire attached to the pin.
The
function
PIN.TOUCH(pin)
can be used to read the touch
value on the pins defined in the table below.
GPIO Pins Available as Touch
Input
|
0
|
2
|
4
|
12
|
13
|
14
|
15
|
27
|
32
|
33
|
The
function
PIN.TOUCH(pin)
returns a value that drops as soon
as the pin is touched.
Normal values
are around 70 when not touched and lower than 20 when
touched.
'Pin Touch
example
'Place a
wire on pin 13 and look how the value changes when touching
it
while
1
print pin.touch(13)
pause 100
wend
end
|
Annex32 has 2 DAC output pins with 8
bits resolution which are available to users.
This function is available only on the
pin GPIO25 and GPIO26
The function
PIN.DAC pin,
value can be used to set the output voltage on the
pin.
The output voltage is approximately 0V @
value=0 and 3.3V @ value=255
'DAC output
example
PIN.DAC 25,
128 'Set the pin
25 at ~1.65V
PIN.DAC 26,
64 'Set the pin
26 at ~0.82V
|
NOTE: When using the command
PIN.DAC,the pin is automatically configured as an
Analog Output
The ESP32 contains several H/W interfaces that
can be controlled by Annex32 WI-Fi using specific commands and
functions.
This functionality permits to control the
output duty cycle of any pin, acting like an analog output.
There are 16 channels available where each
channel can be connected to any output pin.
To use it, the function must first be
configured using the command
PWM.SETUP and then the value can be set using the
command
PWM.OUT.
The frequency and the
resolution can be defined individually for each channel.
The resolution can be from
1 to 15 bits.
The maximal frequency is
80000000 / 2^resolution
This table resumes the
maximal frequency available in function of the resolutions and the
associated range:
RESOLUTION (BITS)
|
MAX FREQUENCY
|
VALUE RANGE
|
1
|
40000000
|
0 ... 1
|
2
|
20000000
|
0 ... 3
|
3
|
10000000
|
0 ... 7
|
4
|
5000000
|
0 .. 15
|
5
|
2500000
|
0 .. 31
|
6
|
1250000
|
0 … 63
|
7
|
625000
|
0 … 127
|
8
|
312500
|
0 … 255
|
9
|
156250
|
0 … 511
|
10
|
78125
|
0 … 1023
|
11
|
39063
|
0 … 2047
|
12
|
19531
|
0 … 4095
|
13
|
9766
|
0 … 8191
|
14
|
4883
|
0 … 16383
|
15
|
2441
|
0 … 32767
|
All the output pins can be
used for the PWM (the pins from GPIO0 to GPIO33).
As there are 16 channels,
up to 16 individual output pins can be used.
If using the M5Stack, the
channels 0 and 7 are already reserved and attached to the pins 25
and 32.
In this case the channels
0 and 7 must be avoided.
To setup an output pin as
a PWM output the following command must be used :
PWM.SETUP
pin, channel,
default_value, [,frequency] [,resolution]
For example, to define a
PWM output at 5KHz with 12 bits of resolution on the pin GPIO5 the
command is :
PWM.SETUP 5, 1,
2048, 5000, 12 ‘ pin 5, channel 1, output value at 2048
(50%), 5KHz, 12 bits
As the resolution is set
at 12 bits, the range will be from 0 to 4095 (hence 2048 is
50%).
To define a PWM output at
10KHz with 8 bits on the pin GPIO22, the command is :
PWM.SETUP 22, 2,
128 ‘ pin 22, channel 2, value 128 ( 50%) , freq
10 KHz (default), resolution 8 bits (default)
As soon as the command
PWM.SETUP is done, the PWM output can simply be changed with the
command :
For example, the
command
PWM.OUT 1,
1000
Set the channel 1
(associated with the pin 5 in the previous command) at 1000.
And the command
PWM.OUT 2, 10
Set the channel 2 (associated with the pin 22
in the previous command) at 10
To disconnect the pin from the PWM output the
command is :
PWM.SETUP pin,
OFF.
For example the command
PWM.SETUP 5,
OFF
Disconnect the pin 5 fro the PWM
NOTE for the
M5stack:
The channel 0 is
dedicated to the internal speaker (pin 25)
The channel 7 is
dedicated to the TFT backlight (pin 32)
This functionality exposes the ability to
control RC (hobby) servo motors.
There are no special commands dedicated as the
servo can simply be used by setting a PWM pin with a 50Hz
frequency.
For example, the following command :
PWM.SETUP 17, 1,
150, 50, 12
Defines the pin 17 with the pwm channel 1 with
a default value of 150 (frequency at 50 Hz and resolution at 12
bits).
The output can then be set with the
command
PWM.OUT 1,
307‘ channel 1 set at 90°
A typical servo motor expects to be updated
every 20 ms with a pulse between 1 ms and 2 ms, or in other words,
between a 5 and 10% duty cycle on a 50 Hz waveform. With a 1.5 ms
pulse, the servo motor will be at the natural 90 degree position.
With a 1 ms pulse, the servo will be at the 0 degree position, and
with a 2 ms pulse, the servo will be at 180 degrees. You can obtain
the full range of motion by updating the servo with any value in
between.
Using a 12 bits resolution (max value = 4095
for 20 msec pulse (1/50 Hz)), the theoretical values should be
:
0° -> 1 msec -> 1/20 * 4096 =
205
90° -> 1.5 msec -> 1.5/20 * 4096 =
307
180° -> 2 msec -> 2/20 * 4096 =
409
Generating Tones
This section introduces the functionality of
generating tones using the
PWM.TONE command.
This command provides the means to create
tones using the PWM.
Syntax
The
PWM.TONE command follows a concise syntax:
PWM.TONE channel,
frequency [, duration_ms]
-
channel: Selects a specific PWM channel for tone output.
-
frequency: Specifies the frequency of the generated tone in Hertz
(Hz).
-
duration_ms: (Optional) Defines the duration of the tone in
milliseconds (ms). If omitted, the tone will play indefinitely.
Practical Examples
Example 1:
Single Tone
To generate a continuous tone at 1000 Hz on
channel 1, use:
PWM.TONE 1,
1000
This command establishes a steady 1000 Hz tone
on channel 1.
Example 2:
Timed Tone
For a tone lasting 200 ms at 2000 Hz on
channel 2, apply:
PWM.TONE 2, 2000,
200
Channel 2 will produce a tone at 2000 Hz for a
duration of 200 milliseconds.
Example 3:
Stopping a Tone
To halt an ongoing tone on channel 1, use the
following command
PWM.TONE 1,
0 command
Prior to using PWM.TONE, ensure proper channel
setup using the PWM.SETUP command, for example using
PWM.SETUP 5, 1,
0. (pin 5, channel1, default output at 0)
Be mindful of the channel's frequency range
and resolution limits.
I²S (Inter-IC Sound), is an electrical serial
bus interface standard used for connecting digital audio devices
together. It is used to communicate PCM audio data between
integrated circuits in an electronic device.
The I²S bus separates clock and serial data
signals, resulting in a lower jitter than is typical of
communications systems that recover the clock from the data
stream.
Despite the name similarity, I²S is
unrelated to the bidirectional I²C (I2C) bus.
The bus consists of three lines:
Bit clock line
-
Typically called "bit clock (BCLK)". PIN GPIO5
Word clock line
-
Typically called "left-right clock (LRCLK)" or “Word Select
(WSEL)”. PIN GPIO26
Data line
-
Typically called "serial data (SD)". Can also be called (SDIN,
SDOUT or DATA). PIN GPIO2
The typical use of the I2S is to connect an
external DAC to provide a High Quality stereo sound output.
Using the PLAY.xxx commands, it will be
possible to play MP3 and WAV files directly from the disk.
Any generic I2S DAC can be used.
Annex32 has been successfully tested with the
PC5102A and the UDA1334A
UDA1334A I2S DAC
PCM5102A I2S DAC
NOTE:
This module requires the following setting
(solder joints)
Component side:
Next to the sck connection
Back side:
1 set to L
2 set to L
3 set to H
4 set to L
It is now also possible to use the CODEC
ES8388 using the command
OPTION.ES8388
The M5stack contains an internal speaker
connected, via an audio amplifier, to the pin GPIO25.
This permits the generation of audio signals
using the internal 8 bits DAC.
Using the PLAY.xxx commands, it will be
possible to play MP3 and WAV files directly from the disk.
It is possible to connect an earphone directly
on the output between the pin GPIO25 and the ground but the best is
to connect a little audio amplifier.
NOTE: it is recommended to put a capacitor
( ~100nF) between the GPIO25 and the audio amplifier in order to
remove the DC component from the audio signal.
The I²C bus allows the module to connect to
I²C devices.
I²C uses only two bidirectional open-drain
lines, Serial Data Line (SDA) and Serial Clock Line (SCL), pulled
up with resistors (typically 4.7K to 10K).
The I²C has a 7 bit address space permitting,
theoretically, to connect up to 126 I/O devices.
The maximal number of nodes is limited by the
address space and also by the total bus capacitance of 400 pF,
which restricts practical communication distances to a few meters.
The relatively high impedance and low noise immunity requires a
common ground potential, which again restricts practical use to
communication within the same PC board or small system of
boards.
The current implementation is master mode @
100Khz by default.
The SDA and SCL pins can be freely defined
using the command
I2C.SETUP sda_pin,
scl_pin.
For example, to define pins
21(SDA) and 22(SCL) the command is :
I2C.SETUP 21,
22
It is important to provide correct pullup
resistors on these lines; values between 4.7K to 10K should be
appropriate.
The commands available are :
I2C.BEGIN,
I2C.END, I2C.REQFROM, I2C.SETUP, I2C.WRITE
The functions available are :
I2C.LEN,
I2C.READ, I2C.END
There are also other advanced
functions / commands to simplify exchanges with the i2c
bus.
The advanced commands available
are :
I2C.READREGARRAY,
I2C.WRITEREGBYTE,I2C.WRITEREGARRAY
The advanced functions available are :
I2C.READREGBYTE[9] [10]
The I2C bus can also
be used with the IO Buffers ( look at the dedicated chapter)
As all the devices can have a "not well"
determined address, please find here a little i2c scanner program
which returns the address of all the devices found connected to the
bus
'I2C Address
Scanner
'print in the
console the address of the devices found
I2C.SETUP
21, 22
' set I2C port on pins 21 and
22
for
i
= 0 to 120
i2c.begin i
if i2c.end = 0
then
print "found "; i , hex$(i)
pause 10
end if
next
i
end
|
This is an example of connection of a module
with PCF8574 bought on Ebay at less than 2€
This drawing shows
how this module must be connected to the ESP32.
It provides 8
digital inputs or outputs.
This is an example of code that "drives" this
module:
I2C.SETUP 21,
22 ' set I2C
port on pins 21 and 22
device_address = 32 'set to
module i2c address
'Write from 0
to 255 on the module
'Each pin
will blink at different frequency
For
i
= 0 to 255
PCF8574_write
i
Next
i
'Read all the
inputs
'The result
is printed into the serial console
' put all the
inputs at pullup state
PCF8574_write
255
r
= 0
For
i
= 0 to 1000
PCF8574_read r
Print r
Next
i
End
sub
PCF8574_write(x)
i2c.begin
device_address
i2c.write x
i2c.end
end
sub
sub
PCF8574_read(x)
i2c.begin
device_address
i2c.reqfrom device_address,
1
x
= i2c.read
i2c.end
end
sub
|
This is another example of connection of a
module with ADS1115 bought on Ebay at less than 2€.
This is a 16 Bit ADC 4 channel Module with
Programmable Gain Amplifier.
Because the module already contains two 10K
I2C pullups, no external resistors are required
As this device is quite simple to interface,
it can be directly driven using a “driver” written in basic.
' ADS1115
Driver for Annex
' datasheet
http://www.ti.com/lit/ds/symlink/ads1115.pdf
' ADS1115
Registers
ADS1115_ADDRESS
= &h48
ADS1115_CONV_REG
= 0 : ADS1115_CONF_REG = 1
ADS1115_HI_T_REG
= 2 : ADS1115_LO_T_REG = 3
i2c.setup
21, 22
' set I2C bus
' Set the
ADS1115 with :
'
AINp = AIN0 and AINn = AIN1
'
FSR = ±4.096 V
'
16 SPS
ADS1115_setup 0, 1,
1
' scale in
volt
scale = 4.096 /
32768
v = 0
for
i
= 0 to 100000
ADS1115_read v
' read from the
module
print v *
scale
next
i
end
'---------------------------------------------------------
' INPUT
MULTIPLEX :
' AINp is
the input positive
' AINn is
the input negative
'0 : AINp =
AIN0 and AINn = AIN1
'1 : AINp =
AIN0 and AINn = AIN3
'2 : AINp =
AIN1 and AINn = AIN3
'3 : AINp =
AIN2 and AINn = AIN3
'4 : AINp =
AIN0 and AINn = GND
'5 : AINp =
AIN1 and AINn = GND
'6 : AINp =
AIN2 and AINn = GND
'7 : AINp =
AIN3 and AINn = GND
'GAIN
'0 : FSR =
±6.144 V
'1 : FSR =
±4.096 V
'2 : FSR =
±2.048 V
'3 : FSR =
±1.024 V
'4 : FSR =
±0.512 V
'5 : FSR =
±0.256 V
'6 : FSR =
±0.256 V
'7 : FSR =
±0.256 V
'DATA
RATE
'0 : 8
SPS
'1 : 16
SPS
'2 : 32
SPS
'3 : 64
SPS
'4 : 128
SPS
'5 : 250
SPS
'6 : 475
SPS
'7 : 860
SPS
sub
ADS1115_setup(inp_mux, gain, rate)
local conf
conf = (inp_mux
<< 12) or
(gain << 9)
or (rate << 5)
or 3 ' +
disable comp
'use the IO Buffer 0 for
writing
iobuff.dim(0,2)
= (conf and
&hff00) >> 8
, conf and &hff
i2c.write_iobuff(0),
ADS1115_ADDRESS, ADS1115_CONF_REG
end
sub
sub
ADS1115_read(ret)
'use the IO Buffer 0 for
reading
i2c.read_iobuff(0),
ADS1115_ADDRESS, ADS1115_CONV_REG, 2
if iobuff.len(0)
= 0 then
print "No
communication"
ret
= 0
else
ret
= iobuff.read(0, 0)
<< 8 + iobuff.read(0, 1)
end
if
if ret >
32768 then ret = ret -
65536
end
sub
|
This is another example for connecting an I2C
module, an MCP20S17 bought on Ebay at less than 2€.
This module provides 16 GPIO pins that can be
used as digital inputs or outputs.
Because the module already contains two 10K
I2C pullups, no external resistors are required
As this device is quite simple to interface,
it can be directly driven using a “driver” written in basic.
' MCP23017
Driver for Annex
' datasheet
http://ww1.microchip.com/downloads/en/DeviceDoc/20001952C.pdf
I2C.SETUP
21, 22
' set I2C port on pins 21 and
22
device_address
= 32 'set to module i2c address
'MCP23017
internal registers
IODIRA
= 0 : IODIRB
= 1 : IPOLA = 2 : IPOLB = 3
GPINTENA = 4 : GPINTENB = 5 : DEFVALA =
6 : DEFVALB = 7
INTCONA = 8 : INTCONB = 9 : IOCONA = 10 : IOCONB = 11
GPPUA
= 12 : GPPUB
= 13 : INTFA
= 14 : INTFB
= 15
INTCAPA = 16 : INTCAPB =
17 : GPIOA = 18 :
GPIOB =
19
OLATA
= 20 : OLATB
= 21
i2C.WriteRegByte
device_address, IOCONA,
&h08 ' init MCP23S17 with bit HAEN
i2C.WriteRegByte
device_address, IODIRA,
&hFF ' all PORT A pins as input
i2C.WriteRegByte
device_address,
GPPUA, &hff ' set PORT A pullup on all
pins
i2C.WriteRegByte
device_address, IODIRB,
&h00 ' all PORT B pins as output
r = 0
for
z
= 0 to 255
I2C.WriteRegByte device_address, GPIOB, z
' pulse all GPIOB
pins
r = i2C.ReadRegByte(device_address, GPIOA) ' read
all GPIOA pins
print r
pause 100
next
z
|
The SPI bus allows
the module to connect to SPI devices.
The Serial
Peripheral Interface bus (SPI) is a synchronous serial
communication interface used for short distance communication
between devices.
SPI devices
communicate in full duplex mode using a master-slave architecture
where the ESP32 is the master. The ESP32 generates the frame for
reading and writing.
Multiple slave
devices are supported through selection with individual chip select
(CS) lines.
The SPI bus utilise four
logic signals:
SIGNAL
|
DESCRIPTION
|
I/O
PIN
|
SCLK
|
Serial
Clock (output from the ESP32)
|
GPIO18
|
MISO
|
Master
Input Slave Output (data input to the ESP32)
|
GPIO19
|
MOSI
|
Master
Output Slave Input (data output from the ESP32)
|
GPIO23
|
CS
|
Chip
Select (often active low, output from the ESP32)
|
Any
output pin, controlled automatically
|
Because these
pins are allocated by default, they may not not be available, by
default, to be used as generic GPIO pins.
For that the
command
SPI.STOP enable to recover the control on these I/O
pins
CS pin
As many devices can
be connected in parallel, sharing the same SCLK, MISO and MOSI
signals, each device is controlled individually using an individual
CS signal.
As Annex32
implements multitasking, in order to guarantee that the CS signal
is generated in phase with the data to be transferred, the CS pin
is managed automatically.
The command
SPI.CSPIN pin [,
polarity] permits the pin associated with the device to
be controlled. Additionally, it permits to define the
polarity as 0 = active low (default) and 1 = active high.
SPI
Mode: Polarity and Clock Phase
The SPI
interface defines no protocol for data exchange, limiting overhead
and allowing for high speed data streaming. Clock polarity (CPOL)
and clock phase (CPHA) can be specified as ‘0’ or ‘1’ to form four
unique modes to provide flexibility in communication between master
and slave as shown below :
If CPOL
and CPHA are both ‘0’ (defined as Mode 0) data is sampled at the
leading rising edge of the clock. Mode 0 is by far the most
common mode for SPI bus slave communication.
If CPOL
is ‘1’ and CPHA is ‘0’ (Mode 2), data is sampled at the leading
falling edge of the clock. Likewise, CPOL = ‘0’ and CPHA = ‘1’
(Mode 1) results in data sampled on the trailing falling edge and
CPOL = ‘1’ with CPHA = ‘1’ (Mode 3) results in data sampled on the
trailing rising edge.
The
table below summarizes the available modes.
Mode
|
CPOL
|
CPHA
|
0
|
0
|
0
|
1
|
0
|
1
|
2
|
1
|
0
|
3
|
1
|
1
|
The data can also
be sent MSB first or LSB first.
This is defined as
bit order and is MSB first by default
Even if the chip is
able to achieve 80Mhz, the maximum realistic SPI speed is
40Mhz.
The commands available are :
SPI.SETUP speed
[,data_mode [, bit_order]]
SPI.CSPIN pin [,
polarity]
The functions available are :
ret =
SPI.BYTE(byte)
a$ =
SPI.STRING$(data$,
len)
a$ =
SPI.HEX$(datahex$,
len)
As said
previously, because the ESP32 uses multitasking, it is
impossible to warrant the exclusive use of the SPI bus during the
execution of the script (it could be used by the SD card or the
TFT, for example).
For this reason, the
CS pin is managed internally by Annex directly by the SPI
functions.
This is defined with
the command
SPI.CSPIN pin [,
polarity]
The optional
parameter polarity
defines if the CS signal must be active low (0 = default) or active
high (1).
This command will set
the pin automatically as output.
The SPI bus can also
be used with the IO Buffers ( look at the dedicated chapter)
Look at the examples
below for more details:
This is an example of connection of a module
with 74HC595 bought on Ebay at less than 2€
This drawing shows
how this module must be connected to the ESP8266.
It provides 8
digital outputs.
This is an example
of code that "drives" this module:
'Write from 0
to 255 on the module
'Each pin
will blink at different frequency
spi.setup
100000
' set the SPI port at
100KHz
SPI.CSPIN 15,
1 ' defines the pin 15 as CS active
high
for
i
= 0 to 255
r
= spi.byte(i)
next
i
end
|
This is another example for connecting an SPI
module, an MCP23S17 bought on Ebay at less than 2€.
This module provides 16 GPIO pins that can be
used as digital inputs or outputs.
As this device is quite simple to interface,
it can be directly driven using a “driver” written in basic.
This is an example using the SPI pins and the
GPIO15 as CS signal
' MCP23S17 Driver for Annex
' datasheet
http://ww1.microchip.com/downloads/en/DeviceDoc/20001952C.pdf
spi.setup 1000000
'MCP23S17 SPI address
MCP23S17_ADDR
= &h40 '
assumes A2, A1, A0 to GND
'MCP23S17 internal registers
IODIRA
= 0 : IODIRB
= 1 : IPOLA = 2 : IPOLB = 3
GPINTENA
= 4 : GPINTENB = 5 : DEFVALA =
6 : DEFVALB = 7
INTCONA
= 8 : INTCONB
= 9 : IOCONA = 10 : IOCONB = 11
GPPUA
= 12 : GPPUB
= 13 : INTFA
= 14 : INTFB
= 15
INTCAPA
= 16 : INTCAPB = 17 : GPIOA = 18 : GPIOB = 19
OLATA
= 20 : OLATB
= 21
MCP23S17_WRITE
IOCONA, &h08 ' init MCP23S17 with bit HAEN
MCP23S17_WRITE
IODIRA, &h00 ' all PORT A pins as output
MCP23S17_WRITE
IODIRB, &hff ' all PORT B pins as input
MCP23S17_WRITE
GPPUB , &hff ' all PORT B pins as pullup
v
= 0
for i
= 0 to 255
for z
= 0 to 255
MCP23S17_WRITE GPIOA, z ' pulse all
GPIOA pins
MCP23S17_READ GPIOB, v '
read all GPIOB pins
print v
next z
next i
End
' function for read / write the MCP23S17
sub MCP23S17_WRITE(register,
value)
SPI.CSPIN
15
a
= SPI.byte(MCP23S17_ADDR)
a
= SPI.byte(register)
a
= SPI.byte(value)
end
sub
sub
MCP23S17_READ(register, value)
local a
SPI.CSPIN 15
a = SPI.byte(MCP23S17_ADDR or 1)
a = SPI.byte(register)
value = SPI.byte(0)
end
sub
|
A Controller Area Network (CAN bus) is a
robust vehicle bus
standard designed to allow microcontrollers
and devices to communicate with each other's applications without a
host computer.
It is a message-based
protocol, designed originally for multiplex
electrical wiring within automobiles to save on copper, but can
also be used in many other contexts.
The Annex32 implements the support for
standard CAN 2.0A (11-bit identifier) and CAN 2.0B (29-bit
identifier).
To interface with an external CAN BUS, the
ESP32 requires the use of a CAN transceiver like the
TJA1050, the MCP2551 or the SN65HVD230.
For example this module is based on the chip
TJA1050 and can be bought for around 1€ on the internet.
Annex32 supports the transmission and
reception of CAN BUS frames.
The bus can be initialised in NORMAL mode, NO
ACK mode and LISTEN ONLY mode
The following BUS speeds are supported 25, 50,
100, 125, 250, 500, 800, 1000 (Kbits / sec).
To use the CAN BUS, it must be initialised
before using the function
CAN.SETUP :
ret
= CAN.SETUP(speed, pin_tx, pin_rx, [can_mode [,
filter_code, filter_mask]])
where :
speed
is the bus speed in Kbit/sec and can be 25, 50, 100, 125, 250, 800,
1000
pin_tx
is the pin of the ESP32 used for the TX signal (connected to the
transceiver)
pin_rx
is the pin of the ESP32 used for the RX signal (connected to the
transceiver)
can_mode
can be 0 (NORMAL), 1 (NO ACK) or 2 (LISTEN ONLY). Optional,
defaults to 0 (NORMAL)
filter_code
is the reception filter code. Optional, defaults to &h00000000
(32 bits)
filter_mask
is the reception filter mask. Optional, defaults to &hFFFFFFFF
(32 bits)
The function returns 0 if OK or another value
in case of error
can_mode
represents how the ESP32 interfaces with the BUS
can_mode
|
DESCRIPTION
|
NORMAL
|
Normal operating mode where CAN controller can
send/receive/acknowledge messages
|
NO
ACK
|
Transmission does not require acknowledgment.
Use this mode for self testing
|
LISTEN ONLY
|
The CAN controller will not influence the bus
(No transmissions or acknowledgments) but can receive messages
|
filter_code
and filter_mask can be used to filter messages of a
particular ID.
[11]
Example:
ret
= CAN.SETUP(500, 4, 5, 1) 'Set the bus
at 500Kb/sec in NO ACK mode using the pins 4 and
5
Alternatively, the CAN BUS can be initialised
using the function
CAN.INIT; this function has the same functionality such
as the
CAN.SETUP but gives more control on the BUS timing
(for advanced users) :
ret
= CAN.INIT(pin_tx, pin_rx, can_mode, brp, tset_1,
tseg_2, sjw, triple_sampling, [filter_code,
filter_mask])
where :
pin_tx
is the pin of the ESP32 used for the TX signal (connected to the
transceiver)
pin_rx
is the pin of the ESP32 used for the RX signal (connected to the
transceiver)
can_mode
can be 0 (NORMAL), 1 (NO ACK) or 2 (LISTEN ONLY)
brp
is the Baudrate prescaler (i.e., APB clock divider) can be any even
number from 2 to 128
tset_1
is the Timing segment 1 (Number of time quanta, between 1 to
16)
tset_2
is the Timing segment 2 (Number of time quanta, 1 to 8)
sjw
is the Synchronization Jump Width (Max time quanta jump for
synchronize from 1 to 4)
triple_samp
can be 1 to enable the triple sampling when the CAN controller
samples a bit
filter_code
is the reception filter code. Optional, defaults to &h00000000
(32 bits)
filter_mask
is the reception filter mask. Optional, defaults to &hFFFFFFFF
(32 bits)
The function returns 0 if OK or another value
in case of error
[12]
Example:
PRINT
CAN.INIT(4,
5, 2, 8, 15, 4, 3, 0)
'Set the bus at 500Kb/sec in LISTEN
ONLY mode using the pins 4 and 5
The function
CAN.STOP permits to stop the driver disconnecting it
from the BUS :
ret
= CAN.STOP
The function returns 0 if OK or another value
in case of error
The function
CAN.WRITE permits to write a message on the BUS; the
message can be composed from 0 to 8 bytes.
ret
= CAN.WRITE( id, can_flags [,b0 [,b1 [,b2 [,b3 [,b4
[,b5 [,b6 [,b7 ]]]]]]]])
where :
id
is the ID of the message to be sent; must be 11 bits or 29 bits for
extended messages
can_flags
is used to indicate the type of
message transmitted/received.
b0 ...
b7 message
bytes. Any combination from 0 to 8 bytes can be specified
The function returns 0 if OK or 1 of
message not sent correctly
can_flags
represents the type of message transmitted/received.
can_flags
|
DESCRIPTION
|
0
|
No message flags (Standard Frame Format)
|
1
|
Extended Frame Format (29bit ID)
|
2
|
Message is a Remote Transmit Request
|
4
|
Transmit as a Single Shot Transmission
|
8
|
Transmit as a Self Reception Request
|
16
|
Message's Data length code is larger
than 8. This will break compliance with CAN2.0B
|
Example:
PRINT
CAN.WRITE(
&h12345678,
1, 10, 20, 30) 'Send an extended message with ID &h12345678
composed of 3 bytes (10, 20, 30)
The message can also be sent using the
IOBUFFERS with the function
CAN.WRITE_IOBUFF :
ret
= CAN.WRITE_IOBUFF(bufnum)
The function returns 0 if OK or 1 of
message not sent correctly
The iobuffer must be set before and must
contains from 5 to 13 bytes structured as below :
IOBUFFER
BYTE
|
DESCRIPTION
|
0
|
Message ID Byte 0 (Most significant byte)
|
1
|
Message ID Byte 1
|
2
|
Message ID Byte 2
|
3
|
Message ID Byte 3 (Least significant byte)
|
4
|
Can_flags defined as the previous table
|
5 to
12
|
Message bytes ( can span from 5 to 12 for
message bytes 0 to 8)
|
Another event,
ONCANBUS , has been included to determine when a CAN
message has been received.
ONCANBUS canbus_received
Inside this event, it is possible to read and
analyse the content of the message received.
When this event occurs, the following
functions are available :
CAN.IDENT
The function
CAN.IDENT that returns the ID of the message
received
Example:
id
= CAN.IDENT
CAN.FLAGS
The function
CAN.FLAGS that returns the flags of the message
received
Example:
flags
= CAN.FLAGS
CAN.LEN
The function
CAN.LEN that returns the length of the message received
(the content bytes)
Example:
ret
= CAN.LEN
CAN.BYTE
The content of the message can then be read
byte per byte using the function
CAN.BYTE :
ret
= CAN.BYTE(num)
Returns the content of the byte
num from the message received (the content bytes from 0
to 7)
CAN.READ_IOBUFF
The message can also be received using the
IOBUFFERS with the function
CAN.READ_IOBUFF :
ret
= CAN.READ_IOBUFF(bufnum)
Returns the number of bytes received (the
content)
This function “copies” the content of the
received buffer into the buffer bufnum
without any impact on the function
CAN.BYTE.
Example:
'CANBUS example
'init the canbus
'print can.setup(500, 26, 5, 1) ' speed, pin_tx,
pin_rx
' speed can be 25, 50, 100, 125, 250, 500 or 800
Kbits/sec
'can.setup can also have other optional arguments
'print can.setup(500, 4, 5, 0, &h00000000, &hffffffff) '
speed, pin_tx, pin_rx, mode, filter_code,
filter_mask
' the mode can be 0:normal, 1:no_ack_tx,
2:listen_only
print can.init(26,
5, 1, 8, 15, 4, 3, 1, &h0,
&hffffffff) ' pin_tx,
pin_rx, mode, brp, tset_1, tseg_2, sjw, triple_sampling,
filter_code, filter_mask
'prepare an iobuffer containing an iobuffer to be
sent
iobuff.dim(0,
13) = &h12,
&h34, &h56, &h78, 0 , &h33, &h44, &h55, &h66, &h77, &h88, &h99, &haa
'
id id id
id flag b0
b1 b2 b3
b4 b5 b6
b7
'defines the place where jumps when receiving CAN
messages
ONCANBUS
canbussi
for z
= 0 to 1000
' the message can be sent using
iobuffers
'print
can.write_iobuff(0)
'or directly using the command
write
print "tx "; can.write( z, 0, 0,
1,2,3,4,5,6,7,8)
'
id flag bytes 0 - 7
pause 1000
next z
print
"end sending"
wait
'can message reception routine
canbussi:
'the message can be extracted using these functions
print
"canbussi "; hex$(can.ident); " ";
can.flags; " "; can.len;
" bytes ";
'the content can be read byte per byte
for k
= 0 to 7
print can.byte(k),
next k
print
'or the message can be routed into an iobuffer and extracted byte
per byte
'print can.read_iobuff(1)
'print iobuff.len(1)
'print iobuff.read(1, 0), iobuff.read(1, 1),
iobuff.read(1, 2), iobuff.read(1, 3), iobuff.read(1,
4)
return
|
In addition to the previous
functions,Annex implements the support for up-to 32 (from 0 to 31)
dedicated reception buffers that can be read at any pace
individually.
Each buffer has its own filter
and mask and it can be polled without requiring the use of the
interrupt.
The filter / mask defined in
the
CAN.SETUP function will act as a global
filter and will be applicable to all the buffers.
When a new data arrives, it will
be filtered and stored in the corresponding buffer.
If an existing data is already
present, it will not be overridden (the new one will be discarded),
the reason is to avoid any change of data during the processing
phase.
To accept to receive new data,
the buffer must be released with the command
CAN.BUF_CLEAR(buf_num)
These are several additional
functions :
CAN..BUF_FILTER
ret =CAN.BUF_FILTER(buf_no,
code [,mask]) 'set the filter
example
ret =CAN.BUF_FILTER(0,
&h1234)
'defines the ID &h1234 for the buffer 0
ret =
CAN.BUF_FILTER(0,
&h1234, &hf)
'defines the ID &h1234 for the buffer 0 with a mask
of &hf .In this case the addresses accepted will be from
&h1230 to &h123f (last 4 bits at 1)
CAN.BUF_IDENT
ret =
CAN.BUF_IDENT(buf_no)
'returns the ident
CAN.BUF_LEN
ret =CAN.BUF_LEN(buf_no)
'returns the length of the data
received in the buffer
CAN.BUF_FLAGS
ret =
CAN.BUF_FLAGS(buf_no)
'returns the flags
CAN.BUF_BYTE
ret =
CAN.BUF_BYTE(buf_no,
byte_pos)
'returns the byte
CAN.BUF_CLEAR
ret =
CAN.BUF_CLEAR(buf_no)
'clear the buffer and returns 0
The
CAN.BUF_LEN can be used to determine if new
data has arrived and, when the processing of the data is
terminated, the buffer can be released using
CAN.BUF_CLEAR
Example:
'init the canbus
'can.setup can also have other optional arguments
print
can.setup(500,
26, 5, 0,
&h00000000,
&hffffffff)
' speed, pin_tx, pin_rx, mode, filter_code,
filter_mask
' the mode can be 0:normal, 1:no_ack_tx,
2:listen_only
print
can.BUF_FILTER(0,
&h123)
print
can.BUF_FILTER(1,
&h456)
print
can.BUF_FILTER(2,
&h789)
for
z
=
0
to
10000000
if
(can.BUF_LEN(0))
then
print
hex$(can.BUF_IDENT(0)),
can.BUF_FLAGS(0),
can.BUF_LEN(0),
can.BUF_BYTE(0,
0),
can.BUF_clear(0)
end
if
if
(can.BUF_LEN(1))
then
print
hex$(can.BUF_IDENT(1)),
can.BUF_FLAGS(1),
can.BUF_LEN(1),
can.BUF_BYTE(1,
0),
can.BUF_clear(1)
end
if
if
(can.BUF_LEN(2))
then
print
hex$(can.BUF_IDENT(2)),
can.BUF_FLAGS(2),
can.BUF_LEN(2),
can.BUF_BYTE(2,
0),
can.BUF_clear(2)
end
if
next
z
wait
|
The Remote Control Peripheral (RMT) module on the ESP32 is a
versatile and powerful feature designed to handle protocols that
require precise timing, such as infrared remote control
transmissions, LED strip control, and other time-sensitive
communication protocols. Unlike typical digital I/O operations, the
RMT module can generate and decode signals with highly accurate
timing, allowing it to handle various transmission standards with
minimal CPU involvement.
●
Precise timing control for generating and receiving signals
●
Flexible channel configuration
●
Support for continuous transmission using circular buffer mode
●
Carrier modulation and demodulation capabilities
The number of RMT channels, their configuration for transmission
(TX) and reception (RX), and the available buffer sizes depend on
the specific ESP32 chip variant:
ESP32
Variant
|
Total RMT
Channels
|
Buffer Size per
Channel
|
Total RMT
RAM
|
Memory
Management
|
Channel Sync
Capability
|
RX demodulation
Capability
|
ESP32
|
8
|
64 x 32-bit memory
blocks
|
512 x
32-bit
|
Each channel can use up to 64
memory blocks. Channels can share memory blocks if required. Memory
blocks are dynamically allocated to channels.
|
NO
|
NO
|
ESP32-S2
|
4
|
48 x 32-bit memory
blocks
|
192 x
32-bit
|
Each channel can use up to 48
memory blocks. Memory is dynamically allocated, and multiple blocks
can be assigned to a single channel.
|
YES
|
YES
|
ESP32-S3
|
8
4 TX Channels
(0-3)
4 RX Channels
(4-7)
|
48 x 32-bit memory
blocks
|
384 x
32-bit
|
Channels 0-3 are TX only, and
Channels 4-7 are RX only. Each channel can use up to 48 memory
blocks.
Memory allocation is dynamic,
and blocks can be shared among channels to optimise
usage.
|
YES
|
YES
|
ESP32-C3
|
4
2 TX Channels
(0-1)
2 RX Channels
(2-3)
|
48 x 32-bit memory
blocks
|
192 x
32-bit
|
Channels 0-1 are TX only, and
Channels 2-3 are RX only.
Each channel can use up to 48
memory blocks. Memory allocation is dynamic, and blocks can be
shared among channels to optimise usage.
|
YES
|
YES
|
The RMT module on each ESP32 chip variant uses a flexible memory
management system that allows memory blocks to be dynamically
assigned to individual channels based on the application's
needs:
- Memory
Blocks: Each channel is assigned a specific number of memory
items, which store 32-bit wide data per item. Depending on the
variant, each channel can use up to a maximum number of 48 or
64 items (a block). By default each channel is associated with a
single block.
- Dynamic Allocation: The allocation of
memory blocks to channels is dynamic, allowing a channel to be
configured to use multiple memory blocks based on the protocol
requirements. For example, a channel may need more memory blocks to
handle longer data sequences or more complex communication
protocols.
- Channel
Synchronisation: Some ESP32 variants support the
synchronisation between different RMT channels. This feature
enables precise coordination of signal generation across multiple
channels, which is especially useful for controlling multiple
devices simultaneously or handling complex communication protocols
that require synchronised timing.
The RMT module operates with a clock divider that scales the main
system clock down to a lower frequency suitable for precise timing
operations. The default clock source for the RMT module is derived
from the system clock running at 80 MHz. The clock divider
adjusts this frequency to meet the specific timing requirements of
the application.
● The
clock divider value determines how many system clock cycles are
used to produce one RMT clock cycle. The divider starts at 80,
meaning the RMT clock is derived by dividing the 80 MHz system
clock by the divider value. This scaling allows for various timing
configurations, enabling the RMT module to handle different pulse
widths and frequencies.
For example, with a clock divider of 80, the RMT clock frequency is
providing a time resolution of 1 microsecond per
tick. Adjusting the divider allows for finer control over timing
and signal generation.
The RMT module utilises dedicated RAM to store and process signal
data.
Each entry in the RMT RAM is a 32-bit word divided into four
fields, structured as follows:
Field
|
Description
|
Bits
|
level0
|
Level of the first pulse (high/low)
|
1 bit
|
duration0
|
Duration of the first pulse in ticks
|
15 bits
|
level1
|
Level of the second pulse (high/low)
|
1 bit
|
duration1
|
Duration of the second pulse in ticks
|
15 bits
|
Each memory block consists of these four fields, totaling 32 bits
(4 bytes) per item. This structure allows the RMT module to
precisely control pulse lengths and signal states, enabling
accurate signal generation and decoding. Each RMT item represents a
single pulse, with the following fields:
●
Level0: The state of the signal for the first half of the
pulse.
●
Duration0: The length of time the signal remains at Level0.
The value is set in ticks, where each tick is determined by the
clock divider.
●
Level1: The state of the signal for the second half of the
pulse.
●
Duration1: The length of time the signal remains at Level1.
Similar to Duration0, this is set in ticks.
The number of items used depends on the number of pulses that need
to be sent or received. Each channel can store multiple items, up
to the limit specified in the table above.
The minimum value for the duration is zero (0) and is interpreted
as a transmission end-marker.
The ‘duration’ fields represent the length of the pulse in terms of
clock ticks. By specifying the duration, you determine how long the
signal should be held at a particular level before transitioning to
the next state. This allows for the precise timing and modulation
of signals, crucial for tasks such as generating complex waveforms
or encoding data.
For example, using a clock divider of 80, each tick corresponds to
1 microsecond.
Therefore, the Duration0 and Duration1 fields
directly represent pulse lengths in microseconds.
● All
ESP32 variants allow for extending the memory blocks allocated to
certain RMT channels by reducing the total number of available
channels. This feature enables users to configure longer, more
complex signal patterns by increasing the buffer size for specific
channels. For example, on the ESP32, you can configure:
● 8
channels with 64 items each
● 4
channels with 128 items each
● 2
channels with 256 items each
● This
flexibility allows users to balance between the number of channels
and the complexity of signals they can generate or receive. By
reallocating memory, it is possible to tailor the RMT peripheral to
meet the specific needs of the application, whether it involves
fewer channels with longer signal patterns or more channels with
shorter patterns.
Transmit synchronisation in the context of RMT (Remote Control
Transceiver) refers to the ability to start multiple transmit
channels simultaneously with precise timing. This feature is
crucial for applications that require coordinated output across
multiple channels, such as:
●
Controlling complex LED matrices or strips
●
Generating multi-channel waveforms
●
Implementing protocols that require precise timing across multiple
signal lines
The goal of transmit synchronisation is to ensure that multiple RMT
channels begin their transmission at exactly the same moment,
eliminating any timing discrepancies between channels.
Different ESP32 variants have varying levels of support for
transmit synchronisation:
-
ESP32:
This earlier variant does not have built-in hardware support for TX
synchronisation.
-
ESP32-S2, ESP32-S3 and ESP32-C3:
These newer variants include hardware support for TX
synchronisation. They feature a synchronisation manager that can
coordinate the start of transmission across multiple TX channels
with high precision. This hardware-based approach allows for more
accurate and reliable synchronised transmissions.
In the transmitter mode, the RMT module of the ESP32 provides
versatile capabilities for transmitting data. Key features
include:
●
Idle Level (idle_level):
Set the signal level (high or low) on the RMT output when it is
idle. This ensures a consistent signal state between
transmissions.
●
Loop Enable (loop_en):
Enable or disable continuous looping of the data transmission.
●
Carrier Signal: The RMT module supports the use of a carrier
signal to modulate the output. This carrier signal can be
customised with the following parameters:
○
Enable Carrier Signal (carrier_en):
Activate the carrier signal to modulate the output as required.
○
Carrier Frequency (carrier_freq_hz):
Set a specific frequency (e.g., 38 kHz), which is commonly used in
IR communication.
○
Duty Cycle Percentage (carrier_duty_percent):
Adjust the duty cycle of the carrier signal to define the
proportion of time the signal is high within each period. This can
be set to values such as 33% or any other percentage based on
application needs.
○
Carrier Level (carrier_level):
Specify the level (high or low) of the RMT output when the carrier
is active, determining whether the carrier signal starts with a
high or low output.
In the reception mode, the RMT module provides several
configurable parameters to optimise signal reception and
processing:
●
Enable Filter (filter_en):
Activates a filter on the input of the RMT receiver to remove noise
and unwanted signals.
●
Filter Threshold (filter_ticks_thresh):
Sets the threshold for the filter in terms of the number of ticks.
Pulses shorter than this value will be filtered out. The acceptable
range for this value is from 0 to 255 ticks.
●
Idle Threshold (idle_threshold):
Defines a pulse length threshold in ticks that will cause the RMT
receiver to enter an idle state. Pulses longer than this threshold
will be ignored, helping to manage longer, non-relevant
signals.
Additionally, the RMT module can optionally demodulate the
input signal using the following parameters:
●
Enable Carrier Demodulation (rm_carrier):
Activates the carrier demodulation feature to process modulated
signals.
●
Carrier Frequency (carrier_freq_hz):
Specifies the frequency of the carrier signal in Hz, used for
demodulation. This allows the receiver to correctly interpret
signals modulated at specific frequencies.
●
Carrier Duty Cycle (carrier_duty_percent):
Sets the duty cycle of the carrier signal in percentage, which
helps in accurately decoding the modulated signal.
●
Carrier Level (carrier_level):
Defines the level of the RMT input where the carrier is modulated,
indicating whether the carrier signal affects the input when
active.
The RMT uses the
OnInfrared event for the reception of RX data. This
event is triggered immediately when the RMT module receives
incoming data.
RMT.SETUP_TX
channel,
pin [, clk_div] [, mem_block_num] [, idle_level] [, loop_en] [,
carrier_en] [, carrier_freq_hz] [, carrier_duty_percent] [,
carrier_level]
○
Purpose: Configures an RMT channel for transmission.
○
Arguments:
■
channel: RMT
channel number (integer)
■
pin:
GPIO pin number (integer)
■
clk_div: Clock
divider (optional integer, default is 80)
■
mem_block_num:
Number of memory blocks (optional integer, default is 1)
■
idle_level: Idle
level for the channel (optional integer, default is RMT_IDLE_LEVEL_LOW)
■
loop_en: Enable
looping (optional boolean, default is false (0))
■
carrier_en: Enable
carrier signal (optional boolean, default is false (0))
■
carrier_freq_hz:
Carrier frequency in Hz (optional integer, default is 38000)
■
carrier_duty_percent:
Carrier duty cycle percentage (optional integer, default is
33)
■
carrier_level:
Carrier level (optional integer, default is RMT_CARRIER_LEVEL_HIGH)
○
Description: Sets up the RMT channel for transmission with
specified parameters. Uninstalls and reconfigures the RMT
driver.
RMT.WRITE
channel,
num_items, name [, wait]
○
Purpose: Sends data items on an RMT channel.
○
Arguments:
■
channel: RMT
channel number (integer)
■
num_items: Number
of items to write (integer)
■
name:
Integer array containing the data items, where each element is a
32-bit word that encodes levels and periods.
■
wait:
Wait for transmission to complete (optional boolean, default is
true)
○
Description: Writes an array of RMT items to the specified
channel. The name parameter
represents an integer array where each element is a 32-bit word
combining both the levels (1-bit) and periods (15-bits each for
high and low durations) required for transmission. Optionally waits
for the transmission to complete.
RMT.ENCODE
channel,
num_items, array() [, wait]
○
Purpose: Encodes data items for transmission on an RMT
channel.
○
Arguments:
■
channel: RMT
channel number (integer)
■
num_items: Number
of items to encode (integer)
■
array:
A two-dimensional array containing the data to encode:
■
array[x][0]: Level
0
■
array[x][1]:
Duration 0
■
array[x][2]: Level
1
■
array[x][3]:
Duration 1
■
wait:
Wait for transmission to complete (optional boolean, default is
true)
○
Description: Encodes data for transmission from a
two-dimensional array where each row represents a data item to be
transmitted. The array must be defined beforehand with levels and
durations structured as shown. The encoded data is then written to
the specified channel, and it optionally waits for transmission to
complete.
RMT.SETUP_RX
channel,
pin [, clk_div] [, mem_block_num] [, invert] [, filter_en] [,
filter_ticks_thresh] [, idle_threshold] [, rm_carrier] [,
carrier_freq_hz] [, carrier_duty_percent] [,
carrier_level]
○
Purpose: Configures an RMT channel for reception.
○
Arguments:
■
channel: RMT
channel number (integer)
■
pin:
GPIO pin number (integer)
■
clk_div: Clock
divider (optional integer, default is 80)
■
mem_block_num:
Number of memory blocks (optional integer, default is 1)
■
invert: Inverts
the polarity of the signal (optional integer, default is
false
(0))
■
filter_en: Enable
filter (optional boolean, default is true (1))
■
filter_ticks_thresh:
Filter ticks threshold (optional integer, default is 100)
■
idle_threshold:
Idle threshold (optional integer, default is 65535)
■
rm_carrier: Remove
carrier signal (optional boolean, default is true)
■
carrier_freq_hz:
Carrier frequency in Hz (optional integer, default is 38000)
■
carrier_duty_percent:
Carrier duty cycle percentage (optional integer, default is
33)
■
carrier_level:
Carrier level (optional integer, default is RMT_CARRIER_LEVEL_HIGH)
○
Description: Configures the RMT channel for reception with
specified parameters. Uninstalls and reconfigures the RMT driver,
sets up a ring buffer for received data, and starts the
reception.
○
Purpose: Reads data items from an RMT channel.
○
Arguments:
■
array:
Integer array where the received data will be stored, with each
element being a 32-bit word that encodes levels and periods.
○
Description: Reads data from the RMT ring buffer and stores
it in an integer array (array). The array
will contain 32-bit words, with each word representing the levels
(1-bit) and periods (15-bits each for high and low durations) that
were received. The array must be pre-defined before use.
○
Purpose: Decodes received data from an RMT channel.
○
Arguments:
■
array:
A two-dimensional array to store the decoded data:
■
array[x][0]: Level
0
■
array[x][1]:
Duration 0
■
array[x][2]: Level
1
■
array[x][3]:
Duration 1
○
Description: Decodes data from the RMT ring buffer into a
two-dimensional array format where each row corresponds to a
decoded item with levels and durations structured as shown. The
array must be pre-defined before use.
○
Purpose: Adds an RMT channel to a group for synchronised
transmission.
○
Arguments:
■
channel: RMT
channel number (integer)
○
Description: Adds the specified RMT channel to a group. This
is used to define a set of channels that should transmit signals
simultaneously. The transmission is synchronised, starting only
when all channels in the group are ready.
○
Purpose: Removes an RMT channel from a group.
○
Arguments:
■
channel: RMT
channel number (integer)
○
Description: Removes the specified RMT channel from a group.
This action stops the channel from being part of a synchronised
transmission group.
RMT.END
channel
○
Purpose: Terminates (unistall) an RMT channel.
○
Arguments:
■
channel: RMT
channel number (integer)
○
Description: Uninstall the specified RMT channel. This
action stops the channel from being active and frees the
corresponding GPIO pin.
The
RMT.ADD_GROUP andRMT.DEL_GROUP
commands manage the synchronisation of multiple RMT channels for
transmission. By adding channels to a group, the user specifies
which channels should start transmitting at the same time. The
transmission begins only when all channels in the group are ready,
ensuring synchronised signal output across multiple channels. When
a writing command is given on the last channel of the group, the
synchronised transmission can commence, providing coordinated
timing for applications that require multiple simultaneous
signals.
This example demonstrates how to use the RMT (Remote Control)
module on an ESP32 to transmit a sequence of pulses using the
RMT.ENCODE command. Let's break down the example to
understand what each part does and how it works.
' RMT example: set a sequence of 3 pulses of 1ms, 2ms, 3ms with
20msec between
dim a(10,5)
a(0,0)
= 1 : a(0,1)
= 1000: a(0,2)
= 0 : a(0,3)
= 20000
a(1,0)
= 1 : a(1,1)
= 2000: a(1,2)
= 0 : a(1,3)
= 20000
a(2,0)
= 1 : a(2,1)
= 3000: a(2,2)
= 0 : a(2,3)
= 20000
a(3,0)
= 0 : a(3,1)
= 0 : a(3,2)
= 0 : a(3,3)
= 0
' channel, pin, clk_div, num_blocks, idle_level, loop_en,
carrier_en, carrier_freq, carrier_duty_percent,
carrier_level
RMT.Setup_TX 0, 47, 80,
1, 0, 1, 1, 38000, 33, 1
RMT.ENCODE 0, 3,
a()
end
|
Pulse
Sequence Setup
The sequence of pulses is defined using a 2-dimensional array named
a in
the example, where each row represents one RMT item containing
pulse levels and durations.
dim a(10,5)
a(0,0)
= 1 : a(0,1)
= 1000: a(0,2)
= 0 : a(0,3)
= 20000
a(1,0)
= 1 : a(1,1)
= 2000: a(1,2)
= 0 : a(1,3)
= 20000
a(2,0)
= 1 : a(2,1)
= 3000: a(2,2)
= 0 : a(2,3)
= 20000
a(3,0)
= 0 : a(3,1)
= 0 : a(3,2)
= 0 : a(3,3)
= 0
|
●
a(n,
m): Represents a 2-dimensional array where:
○
a(0,0)
to a(3,3) define the
pulse sequence.
○
Column Definitions:
■
a(x,0):
Level0
- The logic level of the pulse for the first phase.
■
a(x,1):
Duration0 -
Duration (in ticks) for Level0.
■
a(x,2):
Level1
- The logic level of the pulse for the second phase.
■
a(x,3):
Duration1 -
Duration (in ticks) for Level1.
Explanation of the Sequence
●
a(0,0) to
a(0,3): First
pulse:
○
Level0
= 1 (High)
○
Duration0 = 1000
ticks (1ms)
○
Level1
= 0 (Low)
○
Duration1 = 20000
ticks (20ms gap)
●
a(1,0) to
a(1,3): Second
pulse:
○
Level0
= 1 (High)
○
Duration0 = 2000
ticks (2ms)
○
Level1
= 0 (Low)
○
Duration1 = 20000
ticks (20ms gap)
●
a(2,0) to
a(2,3): Third
pulse:
○
Level0
= 1 (High)
○
Duration0 = 3000
ticks (3ms)
○
Level1
= 0 (Low)
○
Duration1 = 20000
ticks (20ms gap)
●
a(3,0) to
a(3,3):
Terminates the sequence:
○ All
fields set to 0, indicating the end of transmission.
RMT Setup for Transmission
RMT.Setup_TX 0, 47, 80,
1, 0, 1, 1, 38000, 33, 1
|
The
RMT.Setup_TX command configures the transmitter with
specific parameters:
●
0:
Channel number (0).
●
47:
GPIO pin number (47).
●
80:
Clock divider, resulting in a 1 MHz RMT clock.
●
1:
Number of memory blocks.
●
0:
Idle level set to low.
●
1:
Loop enable, which allows the transmission to repeat
indefinitely.
●
1:
Carrier signal enabled.
●
38000:
Carrier frequency in Hz (38 kHz).
●
33:
Carrier duty cycle (33%).
●
1:
Carrier level set to high when the carrier is active.
Encoding and Transmitting the Pulse Sequence
The
RMT.ENCODEcommand encodes the pulse sequence for
transmission with the following parameters:
●
0:
Channel number.
●
3:
Number of items to encode (3 pulses).
●
a():
The name of the array containing the pulse sequence.
Explanation of
the Functionality
This example sets up the RMT module to send a series of three
pulses, each with increasing duration, followed by a 20 ms gap. The
RMT transmitter is configured to loop this sequence
indefinitely.
By using the RMT.ENCODE
command, the user specifies the pulse sequence data, which the
ESP32 will transmit on GPIO pin 47. The transmission repeats in a
loop until explicitly stopped.
Key
Points
●
Customizable Pulse Sequences: The example shows how to
define complex sequences of pulses with varying durations and
levels.
●
Loop Transmission: By setting the loop enable parameter to
1, the
transmission repeats indefinitely, which is useful for applications
that require a constant signal output.
●
Carrier and Idle Control: The carrier is enabled with a
frequency of 38 kHz and a 33% duty cycle, suitable for applications
like IR communication.
This example configures the ESP32's RMT module to transmit a
sinusoidal signal pattern using 256 data items. The waveform is
generated using the sin() function to
create a sequence of pulses that simulate a sine wave, which is
useful for applications like signal generation, testing, or remote
control emulation.
' RMT demo for generating a sinusoidal signal pattern using 256
data items
dim s(256,
4)
for z
= 0 to 256
s(z,0) =
1
s(z,1) =
sin(z/256*PI*2)
* 500 + 501
s(z,2) =
0
s(z,3) =
5000
next z
' channel, pin, clk_div, num_blocks, idle_level, loop_en,
carrier_en, carrier_freq, carrier_duty_percent,
carrier_level
RMT.Setup_TX 0, 47, 80,
6, 0, 1
rmt.ENCODE 0, 256,
s(), 0
end
|
Explanation of
the Example
- Define the Data
Array:
○ A
2-dimensional array s(256, 4) is
defined to store 256 items, where each item represents a part of
the sinusoidal waveform.
○ Each
item contains four fields:
■
s(z,0): Level of
the RMT output (1 or 0).
■
s(z,1): Duration
of the high or low level in clock ticks (calculated using a sine
function).
■
s(z,2): Level of
the RMT output after the initial duration (0 in this case).
■
s(z,3): Duration
of the low level after the initial pulse (fixed at 5000 ticks).
- Populate the Array with Sinusoidal
Data:
○ The
for
loop iterates from 0 to 256 to generate a sinusoidal pattern.
○ The
duration s(z,1) is
calculated using the sin() function to
create a value that oscillates between 1 and 1001, simulating a
sine wave. This is done by multiplying the sine function's result
by 500
and adding 501 to ensure a
minimum duration.
- RMT Setup for Transmission:
○
RMT.Setup_TX
initialises the RMT transmitter on channel 0, using GPIO pin
47, a
clock divider (clk_div) of
80,
and allocates 6 memory blocks
(num_blocks
= 6).
○
Idle_level is set
to 0
○ Loop
mode (loop_en) is
enabled to continuously transmit the configured data.
- Encode and Transmit the Data:
○
rmt.ENCODE encodes
the data array s() with
256
items and sends it on channel 0.
○ The
function prepares the RMT module to transmit the sinusoidal
pattern.
Memory Block
Usage
The example uses 6 memory blocks
to store at least 256 items:
●
Memory Block Capacity: Each block can store either 64
items or 48 items, depending on the ESP32 variant.
○ With
64 items per block, the 6 blocks can store a total of 384
items (6 × 64).
○ With
48 items per block, the 6 blocks can accommodate 288
items (6 × 48).
This ensures that the RMT module has enough capacity to handle the
entire sequence of 256 items required for the sinusoidal pattern
transmission.
Key Features
of This Example
●
Continuous Sinusoidal Waveform Generation: The example
demonstrates how to use the RMT module to generate a continuous
sinusoidal waveform.
●
Flexible Configuration: The setup uses 6 memory blocks to
ensure sufficient storage for the waveform, regardless of whether
the block size is 64 or 48 items.
This example demonstrates how to use the ESP32's RMT module to
transmit synchronised sinusoidal signal patterns using two
different channels (0 and 2). Both channels
are set up to transmit the same sinusoidal waveform, but the
transmissions are synchronised to start simultaneously. This is
useful in applications where multiple signals must be generated in
sync, such as in lighting systems.
' RMT demo for generating a sinusoidal signal pattern using 256
data items
K
= 95 '
95 points
dim s(K,
4)
for z
= 0 to K
s(z,0) =
1
s(z,1) =
sin(z/K*PI*2
- PI/2)
* 500 + 501
s(z,2) =
0
s(z,3) =
5000
next z
' channel, pin, clk_div, num_blocks, idle_level, loop_en,
carrier_en, carrier_freq, carrier_duty_percent,
carrier_level
RMT.Setup_TX 0, 47, 80,
2, 0, 1
RMT.Setup_TX 2, 21, 80,
2, 0, 1
rmt.add_group 0
rmt.add_group 2
rmt.ENCODE 0,
K-1, s(), 0
pause 500
rmt.ENCODE 2,
K-1, s(), 0
end
|
Explanation of
the Example
- Define the Data
Array:
○ The
array s(K,
4) is defined to store K items (where
K =
95), representing a sinusoidal waveform.
○ Each
item in the array has four fields:
■
s(z,0): Level of
the RMT output (1 or 0).
■
s(z,1): Duration
of the high or low level in clock ticks, calculated using the
sin()
function to generate a sinusoidal pattern.
■
s(z,2): Level of
the RMT output after the initial duration (0 in this case).
■
s(z,3): Duration
of the low level after the initial pulse (fixed at 5000 ticks).
- Populate the Array with Sinusoidal
Data:
○ The
for
loop iterates from 0 to K to generate the
sinusoidal pattern.
○ The
duration s(z,1) is
calculated using sin(z/K*PI*2 - PI/2) *
500 + 501, which creates a shifted sine wave oscillating
between 1 and 1001.
- Setup for RMT Transmission:
○
First Channel Setup (Channel
0):
■
RMT.Setup_TX
initialises the RMT transmitter on channel 0, using GPIO pin
47, a
clock divider (clk_div) of
80,
and allocates 2 memory blocks
(num_blocks
= 2).
■ Loop
mode (loop_en) is
enabled to continuously transmit the configured data.
○
Second Channel Setup (Channel
2):
■
Similarly, RMT.Setup_TX
initialises another RMT transmitter on channel 2, using GPIO pin
21,
the same clock divider (clk_div) of
80,
and 2
memory blocks.
■ Loop
mode (loop_en) is also
enabled for this channel.
- Add Channels to the Group for Synchronized
Transmission:
○
rmt.add_group adds
both channels (0 and 2) to a
synchronisation group. This allows the signals from both channels
to be transmitted simultaneously.
○ The
transmission for both channels will start only when all channels in
the group are ready.
- Encode and Transmit the Data:
○
rmt.ENCODE 0,
K, s(), 0 encodes the data array s() with
K
items and sends it on channel 0.
○
After a pause of 500 milliseconds,
rmt.ENCODE 2,
K, s(), 0 encodes the data array s() with the same
K
items and sends it on channel 2.
○ The
transmission will start simultaneously on both channels
Memory Block
Usage
● Each
channel is configured to use 2 memory
blocks.
○ Each
memory block can store 64 or 48 items, depending on
the configuration.
○ With
2
memory blocks, each channel can store a maximum of:
■
128 items (2 × 64) if the block size is 64 items.
■
96 items (2 × 48) if the block size is 48 items.
○ This
configuration is sufficient to store the 95 items
generated for the sinusoidal pattern.
Key Features
of This Example
●
Synchronised Sinusoidal Waveform Transmission: This example
demonstrates how to use two RMT channels to transmit synchronised
waveforms, which is useful in applications requiring precise timing
across multiple outputs.
●
Configurable Waveform: The sinusoidal pattern is generated
dynamically using the sin() function,
and its duration can be easily adjusted by modifying the
parameters.
●
Flexible Grouping: Adding channels to a group (rmt.add_group)
enables simultaneous transmission, which is critical in
applications like synchronised lighting
This example demonstrates how to configure an RMT channel on the
ESP32 to receive an infrared signal from a specified GPIO pin
and decode it into a usable format. The example showcases how to
set up the RMT receiver, handle the incoming signal, and convert it
into a binary value that represents the transmitted IR code.
'RMT example on IR signal decoding
pin.mode 42,
input
rmt.Setup_RX 4,42,80
dim rx(64)
onInfrared doRMT
wait
doRMT:
t
= millis
rmt.decode rx()
print
"RMT", ubound(rx()),
millis-t
res=0
for z=0
to ubound(rx())
'print z, rx(z,0), rx(z,1), rx(z,2),
rx(z |