ANNEX WI-FI RDS
User Manual
Version 1.44.2
© ciccioCB
2022
COPYRIGHT
The Annex firmware including the AnnexToolKit
and this manual are Copyright 2017-2020 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
Adafruit BNO055 Orientation
Sensor library is written by KTOWN is Copyright (c) Adafruit
Industries. It is released under MIT license.
TFT_eSPI display Library is
Copyright © 2017 Bodmer. It is released under FreeBSD
license.
Adafruit NeoPixel library is
Copyright (c) Adafruit. It is released under MIT
license.
Makuna/NeoPixelBus library is
Copyright © by Michael C. Miller. It is released under LGPL
license.
Adafruit PWM Servo Driver
Library is Copyright (c) Adafruit. It is released under MIT
license.
Arduino Library for Dallas
Temperature ICs is Copyright (c) Miles Burton
<miles@mnetcs.com>. It is released under LGPL
license.
OneWire Library is Copyright
1999-2006 Dallas Semiconductor Corporation and Copyright (c) 2007,
Jim Studt.
Adafruit DHT Humidity &
Temperature Sensor Library is Copyright (c) Adafruit. It is
released under MIT license.
ESP8266 and ESP32 Oled Driver
library is Copyright (c) 2016 by Daniel Eichhorn and Copyright (c)
2016 by Fabrice Weinberg. It is released under MIT
license.
ESP8266Ping is Copyright (c)
Daniele Colanardi. It is released under LGPL license.
ESP AsyncTCP library is
Copyright (c) 2016 Hristo Gochkov. It is released under LGPL
license.
ESP AsyncWebServer library is
Copyright (c) 2016 Hristo Gochkov. It is released under LGPL
license.
IRremoteESP8266 library is
Copyright (c) Sebastien Warin, Mark Szabo, Ken Shirriff, David
Conran. It is released under LGPL license.
uRTCLib is Copyright (c) 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.
MQTT library pubsubclient is
Copyright (c) 2008-2015 Nicholas O'Leary. It is released under MIT
license
MFRC522 library is written by
Miguel Balboa.
The Javascript Editor
EditArea is Copyright © 2008 Christophe Dolivet. It is
released under LGPL license.
The base of the interpreter
comes from the original project "MiniBasic" by Malcom
Mclean.
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:
15
Interpreter:
17
Branch
labels
17
Variables:
17
Arrays:
19
Scope of the
variables:
19
Bases of the
language
20
OPERATORS AND
PRECEDENCE
20
Basic internal
keywords:
22
IF command
:
22
FOR loop
23
WHILE WEND
loop
23
DO LOOP
loop
24
SELECT
CASE
25
GOTO
25
GOSUB
26
DATA
26
END
27
EXIT
27
SUB
27
Logical / boolean
Operations
29
ERRORS
HANDLING
30
ONERROR
ABORT
30
ONERROR
IGNORE
30
ONERROR SKIP
[nn]
30
ONERROR
CLEAR
30
ONERROR GOTO [label |
OFF]
30
BAS.ERRLINE
31
BAS.ERRNUM
31
BAS.ERRMSG$
31
HOW the interpreter works with
the HTML code and Objects :
31
HTML
Objects
35
TIMERS
40
EVENTS
40
Button
Event
40
OnHtmlChange
Event
41
OnHtmlReloadEvent
41
OnInfrared
Event
42
OnSerial
Event
42
OnSerial2
Event
42
OnTouch
Event
42
OnUDP
Event
42
OnWgetAsync
Event
43
OnUrlMessage
Event
43
OnEspNowMsg
Event
46
OnEspNowError
Event
47
OnMQTT
Event
47
WiFI
CONNECTIONS
47
PROGRAM
AUTORUN
49
RECOVERY
MODE
50
WATCHDOG
TIMER
50
DATE - TIME
timekeeper
51
Unix Time
functions
52
SPIFFS File
System
52
Ret = FILE.COPY(filename$,
newfile$)
53
Ret =
FILE.DELETE(filename$)
53
Ret =
FILE.EXISTS(filename$)
53
Ret = FILE.RENAME(oldname$,
newname$)
53
Ret =
FILE.SIZE(filename$)
53
Ret$ =
FILE.DIR$(path$)
53
Ret$ = FILE.READ$(filename$,
[line_num] | [start, length])
53
FILE.APPEND filename$,
content$
53
FILE.SAVE filename$,
content$
53
I/O
BUFFERS
54
Read
Operations
56
Write
Operations
56
Special
operations
57
Advanced
operations
57
Bit
operations
58
Buffer
copy
58
Code examples
:
58
DIGITAL
I/O
60
PIN
INTERRUPTS
62
Analog
input
62
Hardware
interfaces:
63
PWM
63
SERVO
63
COUNTERS
64
PID
controllers
65
I2C BUS
67
PCF8574
Module
68
ADS1115
Module
69
MCP23017
Module
72
SPI BUS
73
74HC595
Module
74
MCP23S17
Module
75
LCD DISPLAY USING
I2C
77
OLED
DISPLAY
81
ST7920 LCD
DISPLAY
83
RTC
module
85
PCA9685 (PWM / Servo)
Module
86
TM1637 display
module
88
TM1638 display
module
90
MAX7219 8-Digits 7-segment
display
91
MAX7219 Dot Matrix
Display
92
NeoPixel WS2812B led
strips
95
NeoPixel based WS2812b Dot
Matrix DIsplay
96
TFT DISPLAY
ILI9341
100
TouchScreen
104
TFT DISPLAY
ST7735
104
INFRARED
INTERFACE
109
ULTRASONIC DISTANCE SENSOR
HC-SR04
112
DHT xx Temperature / Humidity
Sensors
113
DS18B20 Temperature
Sensors
115
BNO055 Absolute Orientation
Sensor
116
BME280 Combined humidity and
pressure sensor
118
APDS9960 Digital Proximity,
Ambient Light, RGB and Gesture Sensor
120
RFID MFRC522 RFID cards
reader
122
Writing NUID for UID changeable
card (4 byte UID version)
126
AC LIGHT
DIMMER
126
DIMMER.SETUP pin_in, pin_out,
[,invert [,swap]]
127
DIMMER.DELAY
uSec
127
DIMMER.LIMITS min,
max
127
DIMMER.BRIGHTNESS
val
127
DIMMER.STOP
127
SONOFF B1
LAMP
128
SONOFFB1.INIT
128
SONOFFB1.RGB r, g,
b
128
SONOFFB1.RGB
color
128
SONOFFB1.WHITE cold,
warm
129
TUYA RGBCW
LAMP
129
TUYALAMP.INIT [max_current_RGB,
max_current_WHITE]
129
TUYALAMP.RGB r, g,
b
130
TUYALAMP.RGB
color
130
TUYALAMP.WHITE cold,
warm
130
FTP
131
BAS.FTP$
131
Server data requests (GET and
POST)
132
WGET$(server$, port,
[,header])
133
WGET$(url$
[,header])
133
WPOST$(server$, body$, port
[,header])
133
WPOST$(url$, body$
[,header])
133
WGETASYNC[(] server$, port,
[,header [,ping]] [)]
133
WGETASYNC[(] url$, [,header
[,ping]] [)]
133
MQTT
134
Ret =
MQTT.Setup(server$)
136
Ret =
MQTT.Fingerprint(fingerprint$)
136
Ret = MQTT.Connect(login$,
pass$
[id$])
136
Ret = MQTT.Connect("", "",
[id$])
136
Ret =
MQTT.Disconnect[()]
136
Ret = MQTT.Publish(topic$,
message$)
136
Ret = MQTT.Subscribe(topic$
[,Qos])
136
Ret =
MQTT.UnSubscribe(topic$)
136
Ret =
MQTT.Connected[()]
136
Ret =
MQTT.Status[()]
136
ESP-NOW
138
TELEGRAM (messenger)
support
148
CONVERSION
FUNCTIONS
150
CONVERT.DEGC_TO_F(degC)
150
CONVERT.F_TO_DEGC(degF)
150
CONVERT.TO_IEEE754(num)
150
CONVERT.FROM_IEEE754(iee754_bin)
150
CONVERT.MAP(number, fromLow,
fromHigh, toLow, toHigh)
150
BAS
CONSTANTS
150
BAS.VER
152
BAS.VER$
152
BAS.ERRLINE
152
BAS.ERRNUM
152
BAS.ERRMSG$
152
BAS.FILENAME$
152
BAS.RTCMEM$
152
BAS.SSID$
152
BAS.PASSWORD$
152
BAS.LOAD
152
BAS.RESETREASON
152
OPTION
COMMANDS
153
OPTION.CPUFREQ
80|160
153
OPTION.MAC
mac$
153
OPTION.LOWRAM
memory
153
OPTION.PWMFREQ
freq
153
OPTION.NTPSYNC
153
OPTION.WDT
timeout_msec
153
OPTION.WDTRESET
153
FUNCTIONS:
154
NUMERICAL
FUNCTIONS
154
ABS(number)
168
ACOS(number)
168
ADC
168
APDS9960.SETUP
(mode)
168
APDS9960.READGESTURE
168
APDS9960.AMBIENT
168
APDS9960.RED
168
APDS9960.GREEN
168
APDS9960.BLUE
168
APDS9960.PROXIMITY
168
APDS9960.GESTUREGAIN
(gain)
168
APDS9960.GESTURELED
(intensity)
168
ASC(string$)
168
ASIN(number)
168
ATAN(number)
168
ATAN2(x,
y)
168
BAS.VER
168
BAS.ERRLINE
157
BAS.ERRNUM
157
BME280.SETUP(address)
168
BME280.ALT(qnh)
168
BME280.HUM
168
BME280.QFE
168
BME280.QNH(altitude)
168
BME280.TEMP
168
BNO055.SETUP(
address)
168
BNO055.HEADING
168
BNO055.PITCH
168
BNO055.ROLL
168
BNO055.VECTOR ( param,
axis)
168
BNO055.CALIB
[(param)]
168
CINT(number)
168
CONVERT.DEGC_TO_F(degC)
168
CONVERT.F_TO_DEGC(degF)
168
CONVERT.TO_IEEE754(num)
168
CONVERT.FROM_IEEE754(ieee754_bin)
168
CONVERT.MAP(number, fromLow,
fromHigh, toLow, toHigh)
160
COS(number)
160
COUNTER.COUNT
(cnt)
168
COUNTER.FREQ
(cnt)
168
COUNTER.PERIOD
(cnt)
168
DATEUNIX(date$)
168
DHT.TEMP
168
DHT.HUM
168
DHT.HEATINDEX
168
DISTANCE(pin_trig,
pin_echo)
168
EMAIL from$, to$, subject$,
message$
168
ESPNOW.ADD_PEER(MAC_add$)
168
ESPNOW.BEGIN
168
ESPNOW.DEL_PEER(MAC_add$)
168
ESPNOW.STOP
168
ESPNOW.WRITE(
msg$)
168
ESPNOW.WRITE(
msg$,MAC_add$)
168
EXP(number)
168
FIX(number)
168
FILE.COPY(filename$,
newfile$)
168
FILE.DELETE(filename$)
162
FILE.EXISTS(filename$)
168
FILE.RENAME(oldname$,
newname$)
168
FILE.SIZE(filename$)
162
FLASHFREE
168
FUSION.ANGLE(axis)
168
INSTR([start], string$,
pattern$)
168
I2C.LEN
168
I2C.READ
168
I2C.READREGBYTE (i2c_address,
register)
168
I2C.END
168
INT(number)
168
LEN(string$)
168
LOG(number)
168
MILLIS
168
MQTT.Setup(server$
)
164
MQTT.Fingerprint(fingerprint$)
165
MQTT.Connect(login$, pass$,
[id$])
165
MQTT.Connect("", "",
[id$])
168
MQTT.Disconnect[()]
168
MQTT.Publish(topic$,
message$)
168
MQTT.Subscribe(topic$
[,Qos])
168
MQTT.UnSubscribe(topic$)
168
MQTT.Connected[()]
168
MQTT.Status[()]
168
NEO.GETPIXEL(pos)
167
NEO.RGB(R, G,
B)
168
PI
168
PID1.COMPUTE( current_value,
target_value)
168
PIN(pin_number)
168
PING(host$)
168
POW(x, y)
168
RAMFREE
168
RND(number)
168
SERIAL.LEN
168
SERIAL2.LEN
168
SGN(number)
168
SIN(number)
168
SPI.BYTE(byte)
168
SQR(number)
168
TAN(number)
168
TFT.RGB(r,g,b)
168
TIMEUNIX(time$)
168
TM1638.BUTTONS
168
TOUCH.X
168
TOUCH.Y
168
VAL(string$)
168
WIFI.CHANNEL
168
WIFI.MODE
168
WIFI.NETWORKS ( network$
)
168
WIFI.RSSI
169
WIFI.STATUS
169
WORD.COUNT( string$
[,delimiter$])
169
WORD.FIND( string$, find$
[,delimiter$])
169
STRING
FUNCTIONS
170
BAS.ERRMSG$
181
BAS.FILENAME$
181
BAS.FTP$( host$, login$,
password$, file$, folder$)
181
BAS.PASSWORD$
181
BAS.RTCMEM$
181
BAS.SSID$
181
BAS.VER$
181
BIN$(number)
181
BUTTON$(name$, label [, id]
)
181
CHECKBOX$( variable
[,id])
181
CHR$(number)
181
CSSID$(object_id,
object_style)
181
DATE$[(format)]
181
ESPNOW.ERROR$
181
ESPNOW.READ$
181
ESPNOW.REMOTE$
181
FILE.DIR$[(path$)]
181
FILE.READ$(filename$,[line_num]
| [start, length])
181
HEX$(number)
181
HtmlEventButton$
181
HtmlEventVar$
181
IMAGE$(path
[,id])
181
IMAGEBUTTON$(path, label
[,id])
181
IP$
181
IR.GET$[ (param)
]
181
JSON$(string$,
field$)
181
LCASE$(string$)
181
LED$(variable[$]
[,id])
181
LEFT$(string$,
num)
181
LISTBOX$(variable$, "option1,
option2, option3, ..." [, height]
[,id])
181
METER$(variable, min, max
[,id])
181
MID$(string$, start
[,num])
181
MAC$[ (id)
]
181
MQTT.Message$
181
MQTT.Topic$
181
OCT$(number)
181
PASSWORD$(variable [, id]
)
181
REPLACE$(expression$, find$,
replacewith$)
181
RIGHT$(string$,
num)
181
RTC.DATE$[(format)]
181
RTC.TIME$
181
SERIAL.CHR$
181
SERIAL.INPUT$
181
SERIAL2.CHR$
181
SERIAL2.INPUT$
181
SLIDER$(variable, min, max
[,step] [,id])
181
SPACE$(number)
181
SPI.STRING$(data$,
len)
181
SPI.HEX$(datahex$,
len)
181
STR$ (number [,format$
[,toint]])
181
STRING$(num,
char$)
181
TEMPR$(pin_number
[,ID])
181
TEXTAREA$(variable [, id]
)
181
TEXTBOX$(variable [, id]
)
181
TRIM$(string$)
181
TIME$
181
UCASE$(string$)
181
UDP.READ$
181
UDP.REMOTE$
181
UNIXDATE$(value
[,format])
181
UNIXTIME$(value)
181
URLMSGGET$
([arg$])
181
WGET$( http_server$, port
[,header] )
181
WGET$( url$, [,header]
)
180
WGETRESULT$
180
WPOST$(server$, body$, port
[,header])
181
WPOST$(url$, body$,
[,header])
180
WORD$(string$, position
[,delimiter$])
181
WORD.DELETE$(string$, position
[delimiter$])
181
WORD.EXTRACT$(string$, lead$,
trail$)
181
WORD.GETPARAM$( setting$,
parameter$ [,separator$])
181
COMMANDS:
181
AUTOREFRESH
interval
212
BAS.LOAD
filename$
212
BAS.RTCMEM$ =
val$
212
CLS
212
CSS
style_code$
212
COMMAND
cmd$
212
COUNTER.RESET
cnt
212
COUNTER.SETUP cnt, pin
[,mode]
212
CSSEXTERNAL
file$
212
DATA const1 [,const2]
...
212
DHT.SETUP pin,
model
212
EMAIL.SETUP server$, port,
user_name$, password$ [, debug]
212
EMAILASYNC from$, to$, subject$,
message$
212
FILE.APPEND filename$,
content$
212
FILE.SAVE filename$,
content$
212
FUSION.INIT
212
FUSION.MADGWICK ax, ay, az, gx,
gy, gz
212
FUSION.MADGWICK ax, ay, az, gx,
gy, gz, mx, my, mz
212
FUSION.MAHONY ax, ay, az, gx,
gy, gz, mx, my, mz
212
FUSION.BETA
=
212
FUSION.ZETA
=
212
FUSION.KI
=
212
FUSION.KP
=
212
HTML
code$
212
I2C.SETUP sda_pin, scl_pin
[,freq [,stretch]]
212
I2C.BEGIN
address
212
I2C.END
212
I2C.REQFROM address,
length
212
I2C.READREGARRAY i2c_address,
register, nb_of_bytes, Array()
212
I2C.WRITE
value
212
I2C.WRITEREGBYTE
i2c_address,register, value
212
I2C.WRITEREGARRAY i2c_address,
register, nb_of_bytes, Array()
212
INPUT.TIMEOUT
timeout
212
INPUT["prompt$";]
variable
212
INTERRUPT pin_no, {OFF |
label}
212
IR.INIT pin_rx | OFF [,
pin_tx]
212
IR.SEND type, code$,
bits
212
JSCALL
javaCode$
212
JSCRIPT
script$
212
JSEXTERNAL
file$
212
LCD.INIT address, cols,
rows
212
LCD.CLS
212
LCD.CUSTOM char,
array()
212
LCD.PRINT x, y,
text$
212
LCD.WRITE
char
212
LOCAL var1 [,var2],
...
212
MAXDISPLAY.SETUP
CS_pin
212
MAXDISPLAY.PRINT msg$
[,‘brightness]
212
MAXSCROLL.SETUP nb_devices,
CS_pin [,reverse]
212
MAXSCROLL.PRINT
msg$
212
MAXSCROLL.NEXT
msg$
212
MAXSCROLL.SHOW pos [,
brightness]
212
MAXSCROLL.SCROLL
[brightness]
212
MAXSCROLL.OSCILLATE
[brightness]
212
NEO.PIXEL led_pos, R, G, B [,
disable]
212
NEO.PIXEL led_pos, COLOR [,
disable]
212
NEO.SETUP
[nb_led]
212
NEO.STRIP led_start_pos,
led_end_pos, R, G, B [, disable]
212
NEO.STRIP led_start_pos,
led_end_pos, COLOR [, disable]
212
NEOSCROLL.SETUP nb_devices, pin
[,serpentine]
212
NEOSCROLL.PRINT
msg$
212
NEOSCROLL.NEXT
msg$
212
NEOSCROLL.COLORS
col$
212
NEOSCROLL. NEXTCOLORS
col$
212
NEOSCROLL.SHOW pos [,
brightness]
212
NEOSCROLL.TEXT
msg$
212
NEOSCROLL.SCROLL
[‘brightness]
212
NEOSCROLL.OSCILLATE
[‘brightness]
212
OLED.CLS
212
OLED.INIT orientation
[,model]
212
OLED.REFRESH
fmt
212
OLED.COLOR
color
212
OLED.PIXEL x,
y
212
OLED.LINE x1, y1, x2,
y2
212
OLED.RECT x,y, width, height
[,fill]
212
OLED.CIRCLE x, y, radius [,
fill]
212
OLED.FONT
font_num
212
OLED.PRINT x, y, text$
[background]
212
OLED.IMAGE x, y,
image$
212
ONERROR ABORT or ONERROR IGNORE
or ONERROR SKIP [nn] or ONERROR CLEAR or ONERROR GOTO
label
212
ONESPNOWERROR [label |
OFF]
212
ONESPNOWMSG [label |
OFF]
212
ONGESTURE [label |
OFF]
212
ONHTMLCHANGE [label |
OFF]
212
ONHTMLRELOAD [label |
OFF]
212
ONINFRARED
label
212
OnMQTT
label
212
ONSERIAL [label |
OFF]
212
ONSERIAL2 [label |
OFF]
212
ONTOUCH [label |
OFF]
212
ONUDP [label |
OFF]
212
ONURLMESSAGE [label |
OFF]
212
ONWGETASYNC [label |
OFF]
212
OPTION.CPUFREQ
80|160
212
OPTION.MAC
mac$
212
OPTION.LOWRAM
value
212
OPTION.PWMFREQ
value
212
OPTION.NTPSYNC
212
OPTION.WDT
timeout_msec
212
OPTION.WDTRESET
212
PAUSE
delay
212
PCA9685.SETUP addr
[,freq]
212
PCA9685.SETFREQ
freq
212
PCA9685.PWM pin,
value
212
PID1.INIT Kp, Ki,
Kd
212
PID1.LIMITS min,
max
212
PID1.PERIOD
msec
212
PID1.PARAMS Kp, Ki,
Kd
212
PID1.SETMODE
mode
212
PIN(pin_number) =
val
212
PIN.MODE pin_number, mode
[,PULLUP]
212
PIN.TONE pin, freq
[,duration]
212
PRINT expression[[,;
]expression] ...
212
PRINT2 expression [[,;
]expression] ...
212
PWM(pin_number) =
value
212
READ var1 [,var2]
...
212
REBOOT
212
REFRESH
212
RESTORE
212
RTC.SETTIME Year, Month, Day,
Hours, Minutes, Seconds
212
SERIAL.BYTE ch1 [,ch2] . .
.
212
SERIAL2.BYTE ch1 [,ch2] . .
.
212
SERIAL.MODE baudrate [, bits,
parity, stop]
212
SERIAL2.MODE baudrate, pin_tx,
pin rx [, bits, parity, stop]
212
SERVO id,
value
212
SERVO.SETUP id, pin_number |
OFF
212
SETTIME Year, Month, Day, Hours,
Minutes, Seconds
212
SLEEP
value
212
SPI.SETUP speed [,data_mode [,
bit_order]]
212
SPI.SETMODE
data_mode
212
SPI.SETFREQ
speed
212
ST7920.INIT
CS_pin
212
ST7920.CLS
212
ST7920.REFRESH
fmt
212
ST7920.COLOR
color
212
ST7920.PIXEL x,
y
212
ST7920.LINE x1, y1, x2,
y2
212
ST7920.RECT x,y, width, height
[,fill]
212
ST7920.CIRCLE x, y, radius [,
fill]
212
ST7920.FONT
font_num
212
ST7920.PRINT x, y, text$
[background]
212
ST7920.IMAGE x, y,
image$
212
TM1637.PRINT msg$ [,
brightness]
212
TM1637.SETUP data_pin, clock_pin
[, bit_delay] [, display_type]]
212
TM1638.PRINT msg$ [, brightness
]
212
TM1638.SETUP data_pin,
clock_pin, strobe_pin
212
TM1638.LEDS
val
212
TFT.BMP filename$, [x, y [,
back_color] ]
212
TFT.CIRCLE x, y, radius [,
fill]
212
TFT.FILL
color
212
TFT.INIT CS_pin, DC_pin,
orientation [Display_width, Display_height,
Variant]
212
TFT.LINE x1, y1, x2, y2,
col
212
TFT.PRINT expression [[,;
]expression] ...
212
TFT.RECT x, y, width, height,
color [ [,fill] ,[round_radius] ]
212
TFT.TEXT.COL color
[,backcolor]
212
TFT.TEXT.POS x,
y
212
TFT.TEXT.SIZE
size
212
TIMER0 interval,
label
212
TIMER1 interval,
label
212
TOUCH.SETUP
T_CS_pin
212
TRACE
message
212
UDP.BEGIN(port)
212
UDP.REPLY
msg$
212
UDP.STOP
212
UDP.WRITE ip, port,
msg$
212
URLMSGRETURN msg$
[,content_type$]
212
WAIT
212
WGETASYNC [(] server$, port,
[,header [,ping]] [)]
212
WGETASYNC [(] url$, [,header
[,ping]] [)]
211
WIFI.APMODE SSID$,
password$ [, channel] [, IP$ ,
MASK$]
211
WIFI.AWAKE
211
WIFI.CONNECT SSID$,
password$ [, BSSID$] [, IP$ , MASK$ [,
GATEWAY$]]
211
WIFI.POWER
pow
212
WIFI.SCAN
212
WIFI.SLEEP
212
WLOG expression[[,; ]expression]
...
212
WORD.DELPARAM setting$,
parameter$, [,separator$]
212
WORD.SETPARAM setting$,
parameter$, value$ [,separator$]
212
BASIC
KEYWORDS
213
CASE
215
DIM array(size) [, …] [= init1,
init2, …]
215
DO
215
ELSE
215
END [IF | SELECT |
SUB]
215
ENDIF
215
EXIT {DO | FOR |
SUB}
215
FOR
215
GOSUB [label |
lab$]
215
GOTO [label |
lab$]
215
IF
215
LET var =
expression
215
LOOP
215
NEXT
215
OFF
215
OUTPUT
215
PULLUP
215
REM
215
RETURN
215
SELECT
215
SPECIAL
215
STEP
215
SUB
215
THEN
215
TO
215
UNTIL
215
WEND
215
WHILE
215
Introduction:
Annex WI-Fi RDS (Rapid Development Suite) is a
version of the "basic" language developed to run on low cost
ESP8266 WIFI devices.
It takes from the original concept of ESPbasic
- which I cooperated on and contributed to in the past - but is a
completely re-designed breakaway project designed to offer improved
functionality and reliability.
The intention was to create a Basic
language for the ESP which complies as much as possible with the
original GWbasic / Visual Basic - from which it shares many
concepts / ideas / syntax - and also from the excellent project
"Micromite" of Geoff Graham.
The base of the interpreter comes from the
original project "MiniBasic" by Malcom Mclean.
The text editor comes from the original
project "EditArea" by Christophe Dolivet.
Functionalities:
-
Includes an internal IDE so can be programmed directly using your
web browser without any additional utility (even with your
phone/tablet).
-
Syntax highlighting with context sensitive Help
-
A programmable web server including 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, multidimensional 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
-
Support for ESP-NOW communications
-
Support for MQTT communications
-
Support for FTP communications
-
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)
-
4 Channels PID controller
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 based on Chipset ILI9341 with 320x240 pixels and 16
bits colors
-
TM1637 4 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
-
BME280 Combined humidity and pressure sensor
-
APDS9960 Digital Proximity, Ambient Light, RGB and Gesture
Sensor
-
RFID MFRC522 cards reader
Interpreter:
The basic interpreter works by reading a
script file saved to the esp local disk filing system (SPIFFS).
In order to use less RAM, the user script is
copied from the disk into a dedicated area in the flash memory
where it is executed, so only the list of the program lines, the
branch labels and the list of the user defined subroutines get
loaded into memory.
This is slower compared to other approaches,
but permits maximum memory to be available for user variables and
program use.
Another performance consideration is that the
ESP8266 must be capable of executing several activities in the
background (web server, file server, ..) so needs sufficient free
memory for running such tasks, and those parallel tasks must
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. I tried to run some "1980s BASIC benchmark" taken from
"the back shed" forum and I can say that the performance is around
2 to 4 times slower than a micromite @48 Mhz; but (IMHO) that’s not
bad considering all the additional connectivity and web
functionalities provided by the esp module.
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.
NOTE : The gosub and the call to user
defined subroutines must be used alone on the script line.
Branch labels
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
|
Variables:
The interpreter has 2 types of variables:
-
Floating Point (double precision)
-
String
Floating point variables can store numbers
with decimal point; 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 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:
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 always starts from 0.
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)
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, ….
The same can be done with string arrays.
Example :
DIM
A$(5) =
"zero", "one", "two", "three", "four", "five"
Scope of the variables:
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.
Bases of the language
The keywords recognized by the interpreter can
be defined into 3 classes:
●
Operators
●
Commands
●
Functions
The Operators are symbols that tells 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.
OPERATORS AND PRECEDENCE
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")
Basic internal keywords:
IF command :
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
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)
|
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"
FOR loop
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"
New feature : the variable in the
NEXT statement
can be omitted.
This means that this program is valid :
FOR
i=1 to
5
Print i
NEXT
WHILE WEND loop
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
DO LOOP loop
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
SELECT CASE
The SELECTcan
have the following syntax:
SELECT
CASE expression
CASE exp1 [,exp2],
... [: Statements]
Statements
CASE exp3 [,exp4],
... [: Statements]
CASEfrom
TO to [: 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 5, 6 :
PRINT "case 5 or 6"
CASE
7 TO 10 : PRINT "case 7 to
10"
CASE ELSE:
PRINT "case
else"
END
SELECT
GOTO
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.
GOSUB
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
DATA
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
END
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
EXIT
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.
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
Logical
/ boolean Operations
As the numerical
variables are stored internally as double precision floating
number, 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
permit 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
ERRORS
HANDLING
Annex allows to control and manage errors
happened during the execution of the code.
This is managed with the command ONERROR.
This command defines the action done 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
|
ONERROR
ABORT
|
Displays the error message and abort the
program.
This is the normal behaviour and is the
default when a program starts running.
|
ONERROR
IGNORE
|
Any error will be simply ignored.
As this can make very difficult to debug a
program it should be used wisely.
|
ONERROR
SKIP
[nn]
|
Ignore an error in a number of commands
(specified by the number 'nn') executed following this command.
'nn' is optional, the default if not specified
is one.
After the number of commands has completed
(with an error or not) the behaviour will revert to ONERROR
ABORT.
|
ONERROR
CLEAR
|
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
|
BAS.ERRLINE
|
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.
|
BAS.ERRNUM
|
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.
|
BAS.ERRMSG$
|
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
|
HOW the
interpreter works with the HTML code and Objects :
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" permit 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 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 insures a synchronized content between all the
clients.
HTML Objects
As said previously,
in order to simplify the creation of HTML pages, there are several
functions permitting to generate the html code automatically.
Let’s start with
the button.
A button is an
object that is used to generate 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.
The refresh
interval should not be less than 300 milliseconds (otherwise the
module will be too busy).
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.
This command does
not have the same timing limitation of REFRESH
so the timing can be faster.
The example :
CLS
led = 0
a$ = BUTTON$("Button1", jump1, "but1") '
"but1" is the ID
a$ = a$ +
LED$(led)
HTML
a$
AutoRefresh
100
'sync each 100
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 will 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.
TIMERS
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 and control is passed to
the "timer interrupt routine" until the execution of the return
command.
Then the program continues from the point
where it has been 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.
EVENTS
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, the
pin change interrupts is an asynchronous event as it 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 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 execution continues from
the previous interrupted location.
Button Event
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
|
OnHtmlChange Event
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
|
OnHtmlReloadEvent
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
|
OnInfrared Event
This event is
triggered when a code is received by the infrared receiver.
Refer to chapter
INFRARED INTERFACE for more details.
OnSerial Event
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
|
OnSerial2 Event
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
|
OnTouch Event
This event is
triggered when the TFT screen is touched.
Refer to the
chapter TouchScreen for more details.
OnUDP Event
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
|
OnWgetAsync Event
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.
As the answer is
async, this command will define the place where the program
execution will continue when the message will be 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
|
OnUrlMessage Event
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 permit 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 want sent 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 get
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 receive 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
pwm(12)
= R
pwm(15)
= G
pwm(13)
= 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(12)
= val(UrlMsgGet$("r"))
pwm(15)
= val(UrlMsgGet$("g"))
pwm(13)
= val(UrlMsgGet$("b"))
UrlMsgReturn
UrlMsgGet$()
return
|
Open the input page into another window and
run the program

If you have a ESP-202, you’ll be able to
control the color of the RGB led present on the card.
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
OnEspNowMsg Event
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
|
OnEspNowError Event
This event is
triggered when a ESP-NOW a 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
|
OnMQTT Event
This event is generated when a MQTT message is
received
Example:
....
onmqtt
mqtt_msg
wait
' receive messages from the server
mqtt_msg:
print
"TOPIC : ";
mqtt.topic$
print
"MESSAGE: ";
mqtt.message$
return
|
WiFI CONNECTIONS
At startup, the module tries to
connect to the router with the parameters provided in the page
“Config”.
If the connection is unsuccessful,
it will default to AP (Access Point) mode .
By default, if no parameters are
specified into the “Config” page, the module will default to the IP
address 192.168.4.1 in AP mode 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 not specified, the IP will be given automatically by the Router
DHCP server.
As soon as the module is connected
to the router, it will reconnect automatically if the connection is
lost.
There are several commands /
functions available to manage the WIFI.
The first function is
WIFI.STATUS that 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.
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$)
=
-1
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.
PROGRAM AUTORUN
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.
RECOVERY 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 config.ini.
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 next restart.
WATCHDOG TIMER
A watchdog timer (or simply a WDT) is an
electronic timer that is used to detect and recover from computer
malfunctions. During normal operation, the computer regularly
resets the watchdog timer to prevent it from elapsing, or "timing
out".
If, due to a hardware fault or program error,
the computer fails to reset the watchdog, the timer will elapse and
generate a timeout signal. The timeout signal is used to initiate
corrective action or actions. The corrective actions typically
include placing the computer system in a safe state and restoring
normal system operation.
Annex implements a watchdog timer with the
command
OPTION.WDT timeout_msec.
As soon as the watchdog has been defined, the
command
OPTION.WDTRESET must be executed regularly within the
defined timeout otherwise the module will reset automatically.
At the beginning of the program, It is
possible to know if the module has been reset by the WDT using the
functionBAS.RESETREASON.
Example:
' WDT example
print "reset reason: ";
select
case bas.ResetReason
case 0: print "normal
startup"
case 1: print "hardware
WDT"
case 2: print "exception
reset"
case 3: print "software
WDT"
case 4: print "software
restart"
case 5: print "wake up from
deep sleep"
case 6: print "external
reset"
end
select
option.WDT 1000
' set the WDT at 1
second
print
"WDT reset regularly"
for z
= 1 to 50
print z,
pause 100
option.WDTreset
next z
print
"WDT not reset regularly, so should crash after 1
sec"
for z
= 1 to 50
print z,
pause 100
next z
|
DATE - TIME timekeeper
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 as soon as the timezone is correctly defined
into the [CONFIG] page.
The timezone is defined as a string
likeCET-1CEST,M3.5.0,M10.5.0/3
that describe 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, eg 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 be reset back to 1970 default at
next restart of the module, 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.
Unix Time functions
The following functions
utilises the time following 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 between the 01/01/1970 and the 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
SPIFFS
File System
Annex
includes a SPIFFS file system hosted on the flash memory chip.
It “emulates” a
disk file system enabling 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
|
1M
|
256
Kb
|
2M
|
1MB
|
4M
|
3MB
|
8M
|
7MB
|
16M
|
15MB
|
As the SPIFFS has
been designed to be very light in terms of resources, it comes with
some limitations :
-
First the SPIFFS does not support directories, it just stores a
“flat” list of files. But contrary to traditional filesystems, the
slash character '/' is allowed in filenames, so the functions that
deal with directory listing (e.g. FILE.DIR$("/website")) basically
just filter the filenames and keep the ones that start with the
requested prefix (/website/). Practically speaking, that makes
little difference though.
-
Second, there is a limit of 31 chars in total for filenames.
-
Combined, that means it is advised to keep file names short and not
use deeply nested directories, as the full path of each file
(including directories, '/' characters, base name, dot and
extension) has to be 31 chars at a maximum. For example, the
filename /website/images/bird_thumbnail.jpg is 34 chars and will
cause some problems if used, for example in FILE.EXISTS() or in
case another file starts with the same first 31 characters.
Warning: That limit is easily reached and if ignored,
problems might go unnoticed because no error message will appear at
runtime.
All the file
related functions share the same prefix FILE.
followed by the specific function.
FUNCTIONS / COMMANDS
|
DESCRIPTION
|
Ret = FILE.COPY(filename$, newfile$)
|
Copy the file filename$
into the file newfile$
Returns 1 in case of success or
0 if error
|
Ret = FILE.DELETE(filename$)
|
Delete the file specified by filename$
Returns 1 in case of success or
0 if error
|
Ret = FILE.EXISTS(filename$)
|
Returns 1 if filename$
exists, otherwise returns 0
|
Ret = FILE.RENAME(oldname$, newname$)
|
Rename the file oldname$
to newname$
Returns 1 in case of success or
0 if error
|
Ret = FILE.SIZE(filename$)
|
Returns the size of the file (in bytes) if the
file exist, otherwise returns -1
|
Ret$ = FILE.DIR$(path$)
|
Will search for files and return the names of
entries found.
path$
represents the directory name.
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.
|
Ret$ = 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.
|
FILE.APPEND
filename$,
content$
|
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 SPIFFS
memory, but the length of filename$
is limited to 31 characters including folder and "/" separators
|
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 SPIFFS
memory, but the length of filename$
is limited to 31 characters including folder and "/" separators
|
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")
|
I/O
BUFFERS
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 surpasses the limit of the Strings that cannot
hold 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
destroyed 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)
Read Operations
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 receive all
the data available from the serial port 2 in the buffer 3 :
SERIAL2.READ_IOBUFF(3)
This command receive the data coming from an
UDP connection in the buffer 1:
UDP.READ_IOBUFF(1)
Additionally some other arguments may be
required.
This command read 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 read 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 read 32 bytes from the SPI bus in
the buffer 2 :
SPI.READ_IOBUFF(2),
32
Write Operations
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 send 10
bytes from the buffer 1 starting from the position 45 to the serial
port :
SERIAL.WRITE_IOBUFF(1,
45, 10)
This command send the complete buffer 1 to the
serial port 2
SERIAL2.WRITE_IOBUFF(1)
This command send 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 send 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(1,
64, 12), “192.168.1.89”,
8080
This command send the entire buffer 2 on the
same UDP device :
UDP.WRITE_IOBUFF(2),
“192.168.1.89”, 8080
This command write 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 send 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
Special operations
The syntax
.REPLY_IOBUFF(buff_num
[, start [, size]])
defines some kind of special operations.
For example, this command send the buffer 0
back to the UDP message original transmitter :
UDP.REPLY_IOBUFF(0)
This is the equivalent of
UDP.REPLY message$
Optionally it is also possible specify part of
the buffer and the destination port 5001 as below:
UDP.REPLY_IOBUFF(0,
2, 6), 5001
When used with the SPI bus, it enables it to
transmit and receive at the same time.
As this operation requires 2 buffers, both
must be specified.
For example, this command send the buffer 0
and receive into the buffer 2:
SPI.REPLY_IOBUFF(0),
(2)
This command send 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)
Advanced operations
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 an hex 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)
Bit operations
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
Buffer copy
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)
Code examples :
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
|
DIGITAL
I/O
Pin numbers
correspond directly to the ESP8266 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 5,
INPUT
To define the pin 4
as input with a pullup:
PIN.MODE 4,
INPUT, PULLUP
To define the pin 2
as output
PIN.MODE 2,
OUTPUT
Although pin numbers can be from 0 to 16, 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.
Digital pins 0 to 5
and 12 to 15 can be
INPUT,
OUTPUT, or
INPUT,
PULLUP .
Pin 16 can be
INPUT,
OUTPUT. At startup, all available pins are
configured as
INPUT.
Pins may also serve
other functions, like Serial, I2C, SPI.
These functions are
normally activated by the corresponding library.

This diagram
shows pin mapping for the popular ESP-12F module.
(*) pins not
available.
The value from a
pîn can read as below :
A
= PIN(5)‘ read from pin GPIO5
The pin value can
be set as below
PIN(2)=
0
‘
set 0 on the pin
GPIO2
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
As many modules use
the NodeMCU pin numbering, please use the table below to find the
corresponding reference:
NodeMCU and Wemos Pin
Reference
|
Standard GPIO Reference
|
D0
|
16
|
D1
|
5
|
D2
|
4
|
D3
|
0
|
D4
|
2
|
D5
|
14
|
D6
|
12
|
D7
|
13
|
D8
|
15
|
D9
|
3
|
D10
|
1
|
In order to use the
Wemos / NodeMcu references, it could be useful to add this code to
create the pin constants at the beginning of the program :
D0=16:D1=5:D2=4:D3=0:D4=2:D5=14:D6=12:D7=13:D8=15:D9=3:D10=1
Then refer to the
pin constants as below :
a
= PIN(D1)
PIN(D3)
= 1
The pins GPIO1
(TXD0) and GPIO3 (RXD0) can be used as normal GPIO pins simply
declaring them as input or output using the command
PIN.MODE .
However, they can
be restored at their normal function using the same command with
the keyword
SPECIAL :
PIN.MODE 1,
INPUT ' set pin 1 (TX) as input
PIN.MODE 3, OUTPUT ' set pin 3
(RX) as output
PIN.MODE 1,
SPECIAL ' restore the pin 1 as (TX)
PIN.MODE 3,
SPECIAL ' restore the pin 3 as (RX)
PIN INTERRUPTS
The
INTERRUPT command permit 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.
Example:
pin.mode 12,
input ' set pin 12 as input
interrupt 12, change_input
' set interrrupt 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
|
Analog input
ESP8266 has a single ADC
channel available to users. It can be used to read voltage at ADC
pin.
To read the voltage
applied at the pin, the function ADC can be used as below :
print
ADC
The voltage range is
0 ... 1.0V and the corresponding range returned by the function is
0 … 1024.
Some modules (like the
NodeMCU) have a voltage divider that modify this range.
In particular, the NodeMCU
have a full scale range of 3.3V.
Hardware interfaces:
The ESP8266 contains several H/W interfaces
that can be controlled by Annex WI-Fi Basic using specific commands
and functions.
PWM
This functionality permits to control the
output duty cycle of any pin, acting like an analog output.
The pin can be any available pin and the range
of the value can be from 0 to 1023.
To use it, there is no need to configure the
pin, the command PWM can be used directly as below:
PWM(12)
= 512
To disable PWM :
PWM(12)
= 0
The default PWM Frequency
is 1000 Hz but can be changed using the command
OPTION.PWMFREQ freq
where freq can be any value from 1 Hz to 40000 Hz
NOTE :
The ESP doesn't have
hardware PWM, so the implementation is by software.
With one PWM output at
40KHz, the CPU is already rather loaded. The more PWM outputs used,
and the higher their frequency, the closer you get to the CPU
limits, and the less CPU cycles are available for sketch
execution.
TONE
It is possible to generate a tone on any pin
using the command
PIN.TONE.
The syntax is
PIN.TONE pin, freq
[, duration]
The pin can be any valid GPIO, the frequency
can be between 1 and 5000Hz, the optional parameter duration define
the duration of the tone in msec; if not defined the tone will last
forever and can be removed using the same command with frequency
equal to 0.
Example :
PIN.TONE 14, 1000,
200 ‘ tone of 1000Hz on pin 14 for 200
msec
PIN.TONE 14,
100 ‘ tone of 100Hz non stop
PIN.TONE 14,
0 ‘ clear the tone
SERVO
This functionality exposes the ability to
control RC (hobby) servo motors. It support up to 4 servos on any
available pin.
While many RC servo motors will accept the
3.3V IO data pin from a ESP8266, most will not be able to run off
3.3v and will require another power source that matches their
specifications. Make sure to connect the grounds between the
ESP8266 and the servo motor power supply.
Before using it, the servo channel (from 1 to
4) and the pin must be defined.
This can be done using the command
SERVO.SETUP id,
pin as below :
SERVO.SETUP 1,
12 ' attach the servo #1 to pin
GPIO12
SERVO.SETUP 2,
13 ' attach the servo #2 to pin
GPIO13
After the definition, the servo can be
controlled with the command SERVO value as below:
SERVO 1,
90 ' set the servo 1 at 90°
SERVO 2,
45 ' set the servo 2 at 45°
The value must be within the range from 0 to
180
To detach the servo from the pin, use the
command SERVO.SETUP id, OFF as below :
SERVO.SETUP 1,
OFF
SERVO.SETUP 2,
OFF
COUNTERS
This functionality exposes two
counters that permit to count pulses from any input pin.
Each counter can return the
frequency of the signal using a timeframe of one second.
Additionally, each counter can
also return the time occurred between consecutive pulses (period);
this is useful as this will permit to determine the frequency of
low frequency signals simply taking its reciprocal
(f = 1 / period).
These counters are not based on
H/W but are managed using processor interrupts so the input
frequency should be limited to around 10 Khz.
It is possible to define when the
pulse is counted (on Rising Edge, on Falling Edge, on
Change).
The “on Change” is particularly
interesting when associated with low frequency measurement as it
will permit to double the number of pulses.
Before using the counters, the
input pin must be set as INPUT using the command
PIN.MODE.
To start to use the counters, the
following command is available:
COUNTER.SETUP cnt, pin
[,mode]
where:
‘cnt’ defines which counter (1 or 2)
‘pin’ defines the input pin (can be any valid
pin except GPIO16)
‘mode’ can be 1 (rising edge), 2 (falling
edge) or 3 (change). If not specified, the mode change is
enabled.
Example:
PIN.MODE 12,INPUT
‘defines the pin GPIO12 as INPUT
COUNTER.SETUP 1, 12,
1 ‘ Counter 1 using pin GPIO12, count on Rising
Edge
The pulses counted can be read
using the function
COUNTER.COUNT(cnt).
The frequency can be read using
the function
COUNTER.FREQ(cnt).
The period between 2 consecutive
pulses can be read using the function
COUNTER.PERIOD(cnt).
Example:
print
COUNTER.COUNT(1)
‘print the pulses counted from the counter 1
print
COUNTER.FREQ(1)
‘print the frequency
from the counter 1
print
COUNTER.PERIOD(1)
‘print the period
from the counter 1
FInally the counters can be reset
with the command
COUNTER.RESET cnt
Example:
COUNTER.RESET 1
‘ reset the counter 1
PID controllers
A proportional–integral–derivative controller
(PID controller. or three-term controller) is a control loop
feedback mechanism widely used in industrial control systems and a
variety of other applications requiring continuously modulated
control. (ref wikipedia)
A PID controller continuously calculates an
error value e(t) as the difference between a desired setpoint (SP)
and a measured process variable (PV) and applies a correction based
on proportional, integral, and derivative terms (denoted P, I, and
D respectively), hence the name.
In practical terms it automatically applies
accurate and responsive correction to a control function.
An everyday example is the cruise control on a
car, where ascending a hill would lower speed if only constant
engine power is applied. The controller's PID algorithm restores
the measured speed to the desired speed with minimal delay and
overshoot, by increasing the power output of the engine.
Annex implements 4 PID controllers that can be
used simultaneously for any application.
The commands are :
PIDx.INIT Kp, Ki,
Kd
PIDx.SETMODE mode
PIDx.LIMITS min,
max
PIDx.PERIOD msec
PIDx.PARAMS Kp, Ki,
Kd
The main function is:
Pid_Out
= PIDx.COMPUTE(CURR_VALUE, TARGET_VALUE)
PIDx can bePID1,
PID2, PID3 or
PID4.
The first step is to initialise the PID
controller.
This can be done with the command
PIDx.INIT Kp, Ki,
Kd :
Example:
PID1.INIT 50, 80,
1 'initialise the controller with Kp = 50, Ki = 80 and
Kd = 1
Optionally the output limits can be set using
the command
PIDx.LIMITS min,
max.
If not defined the output is limited between 0
and 255
Example:
PID1.LIMITS 0,
1023 ' limits the output between 0 and 1023
Then the sampling period can be defined using
the command
PIDx.PERIOD msec
If not defined the period is set at 100
msec
Example:
PIDx.PERIOD 50
' define the period at 50 msec
Finally the main function
output =
PIDx.COMPUTE(input,
setpoint)
Example:
Pid_Out
=
PID1.COMPUTE(CURR_VALUE,
TARGET_VALUE)
This function must be called regularly in a
loop; the best is to call it using a timer.
The command
PIDx.SETMODE mode
can be used to put the loop in manual with
PIDx.SETMODE 0.
In this case the PID loop will be stopped and
the output value will be frozen.
To restore the auto mode (default), the
command is
PIDx.SETMODE 1.
At any moment the PID parameters can be
changed using the command
PIDx.PARAMS Kp, Ki,
Kd :
Example:
PID1.PARAMS 30, 80,
10 ' modify the PID parameters with Kp = 30, Ki = 80 and
Kd = 10
The following example shows how control the
speed of a 3-wire 12V PC fan.
The PID utilise the counter to determine the
fans speed controlling it using the PWM.
A little circuit is required to drive the
positive side of the FAN.

CODE:
pid1.bas
' PID Test
program
' cicciocb
2019
' this
example controls the speed of a 3-wire PC fan
' using the
internal counter as input and the PWM as output
PID1.INIT
1, 5,
0 'init the PID
paramters
PID1.LIMITS
0,
1023 'the PWM goes up to
1023
pin.mode
12,
input, pullup 'GPIO12 is the tach input. Pullup is
ON
counter.setup
1, 12,
3 ' count changes so the output
is doubled
cnt_p
= 0 '
previous value of the counter
target
= 50 'the default target speed
(hz)
timer0
100, do_pid
'the PID is updated each
100msec
onHtmlReload
create_web_page
gosub
create_web_page
wait
create_web_page:
cls
a$
= "PID
DEMO PROGRAM<br>"
a$
= a$ + "SPEED (Hz)
" + textbox$(target)
html
a$
return
do_pid:
' The
frequency is computed at each cycle counting the pulses
occurred
' As the
cycle is executed each 100msec, the frequency
computed
' is 1/10 of
the real one
cnt
= counter.count(1)
freq
= cnt - cnt_p ' the
frequency
cnt_p
= cnt
'the
frequency is doubled and divided by 10
'so the real
target is target *2 /10
out
= pid1.compute(freq, target *2 /10)
'compute the PID
print
"pulses ";freq, "freq "; freq *10/2,
"pwm out ";out
pwm(13)
= out
return
|
I2C BUS
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
4(SDA) and 5(SCL) the command is :
I2C.SETUP 4,
5
It is important to provide the correct pullup
resistor on these lines; values from 10K to 100K 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
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
4, 5
' set I2C port on pins 4 and
5
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
|
PCF8574 Module
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 ESP8266.
It provides 8
digital inputs or outputs.

This is an example of code that "drives" this
module:
I2C.SETUP
4, 5
' set I2C port on pins 4 and
5
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
|
ADS1115 Module
This is another example of a 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
dim
BUFF_IN(2)
' buffer used for read/write to
module
i2c.setup
4,5
' 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
|
MCP23017 Module
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
4, 5
' set I2C port on pins 4 and
5
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
|
SPI BUS
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 ESP8266 is the master. The ESP 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 ESP8266)
|
GPIO14
|
MISO
|
Master
Input Slave Output (data input to the ESP8266)
|
GPIO12
|
MOSI
|
Master
Output Slave Input (data output from the ESP8266)
|
GPIO13
|
CS
|
[1] [2] Chip Select (often
active low, output from the ESP8266)
|
Any
pin, controlled by user program
|
The commands available are :
SPI.SETUP speed
[,data_mode [, bit_order]]
SPI.SETMODE data_mode
SPI.SETFREQ speed
The functions available are :
ret =
SPI.BYTE(byte)
a$ =
SPI.STRING$(data$,
len)
a$ =
SPI.HEX$(datahex$,
len)
74HC595 Module
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.
