ANNEX WI-FI RDS
User Manual
Version 1.48
© ciccioCB
2023
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: 18
Interpreter: 19
Branch labels
20
Variables: 20
Arrays: 21
Scope of the variables: 22
Bases of the language
23
OPERATORS AND PRECEDENCE
23
Basic internal keywords: 24
IF command : 24
FOR loop
25
WHILE WEND loop
26
DO LOOP loop
26
SELECT CASE
27
GOTO
28
GOSUB
28
DATA
28
END
29
EXIT
29
SUB
30
Logical / boolean Operations
31
ERRORS HANDLING
32
ONERROR ABORT
33
ONERROR IGNORE
33
ONERROR SKIP [nn]
33
ONERROR CLEAR
33
ONERROR GOTO [label |
OFF]
33
BAS.ERRLINE
33
BAS.ERRNUM
33
BAS.ERRMSG$
33
HOW the interpreter works with the HTML
code and Objects : 34
HTML Objects
38
TIMERS
43
EVENTS
43
Button Event 43
OnHtmlChange Event 44
OnHtmlReloadEvent 44
OnInfrared Event 44
OnSerial Event 44
OnSerial2 Event 45
OnTouch Event 45
OnUDP Event 45
OnWgetAsync Event 46
OnUrlMessage Event 46
OnEspNowMsg Event 49
OnEspNowError Event 49
OnMQTT Event 50
WiFI CONNECTIONS
50
PROGRAM AUTORUN
52
RECOVERY MODE
52
WATCHDOG TIMER
53
DATE - TIME timekeeper 54
Unix Time functions
54
SPIFFS File System
55
Ret
= FILE.COPY(filename$, newfile$)
56
Ret
= FILE.DELETE(filename$)
56
Ret
= FILE.EXISTS(filename$)
56
Ret
= FILE.RENAME(oldname$, newname$)
56
Ret
= FILE.SIZE(filename$)
56
Ret$ =
FILE.DIR$(path$)
56
Ret$ = FILE.READ$(filename$, [line_num] |
[start, length])
56
FILE.APPEND filename$,
content$
56
FILE.SAVE filename$,
content$
56
I/O BUFFERS
57
Read Operations
59
Write Operations
59
Special operations
60
Advanced operations
60
Bit operations
61
Buffer copy
61
Code examples : 61
DIGITAL I/O
63
PIN INTERRUPTS
65
Analog input 65
Hardware interfaces: 66
PWM
66
SERVO
66
COUNTERS
67
PID controllers
68
I2C BUS
70
PCF8574 Module
71
ADS1115 Module
72
MCP23017 Module
75
SPI BUS
76
74HC595 Module
77
MCP23S17 Module
78
LCD DISPLAY USING I2C
80
OLED DISPLAY
84
ST7920 LCD DISPLAY
86
RTC module
88
PCA9685 (PWM / Servo) Module
89
TM1637 display module
91
TM1638 display module
93
MAX7219 8-Digits 7-segment
display
94
MAX7219 Dot Matrix Display
95
NeoPixel WS2812B led strips
98
NeoPixel based WS2812b Dot Matrix
DIsplay
99
TFT DISPLAY ILI9341
103
TouchScreen
107
TFT DISPLAY ST7735
107
INFRARED INTERFACE
112
ULTRASONIC DISTANCE SENSOR
HC-SR04
115
DHT xx Temperature / Humidity
Sensors
116
DS18B20 Temperature Sensors
118
BNO055 Absolute Orientation Sensor
119
BME280 Combined humidity and pressure
sensor 121
APDS9960 Digital Proximity, Ambient
Light, RGB and Gesture Sensor 123
RFID MFRC522 RFID cards reader
125
Writing NUID for UID changeable card (4 byte UID version)
129
AC LIGHT DIMMER
129
DIMMER.SETUP pin_in, pin_out, [,invert
[,swap]]
130
DIMMER.DELAY uSec
130
DIMMER.LIMITS min,
max
130
DIMMER.BRIGHTNESS val
130
DIMMER.STOP
130
SONOFF B1 LAMP
131
SONOFFB1.INIT
131
SONOFFB1.RGB r, g, b
131
SONOFFB1.RGB color
131
SONOFFB1.WHITE cold,
warm
132
TUYA RGBCW LAMP
132
TUYALAMP.INIT [max_current_RGB,
max_current_WHITE]
132
TUYALAMP.RGB r, g, b
133
TUYALAMP.RGB color
133
TUYALAMP.WHITE cold,
warm
133
FTP
134
BAS.FTP$
134
Server data requests (GET and POST)
135
-
WGET$(server$, port,
[,header])
136
-
WGET$(url$
[,header])
136
-
WPOST$(server$, body$, port
[,header])
136
-
WPOST$(url$, body$
[,header])
136
-
WGETASYNC[(] server$, port,
[,header [,ping]] [)]
136
-
WGETASYNC[(] url$, [,header
[,ping]] [)]
136
MQTT
137
Ret
= MQTT.Setup(server$)
139
Ret
= MQTT.Fingerprint(fingerprint$)
139
Ret
= MQTT.Connect(login$, pass$ [id$])
139
Ret
= MQTT.Connect("", "", [id$])
139
Ret
= MQTT.Disconnect[()]
139
Ret
= MQTT.Publish(topic$, message$)
139
Ret
= MQTT.Subscribe(topic$ [,Qos])
139
Ret
= MQTT.UnSubscribe(topic$)
139
Ret
= MQTT.Connected[()]
139
Ret
= MQTT.Status[()]
139
ESP-NOW
141
TELEGRAM (messenger) support 151
PEEK and POKE FUNCTIONS
153
BAS.PEEK(addr)
153
BAS.PEEK16(addr)
153
BAS.PEEK8(addr)
153
BAS.POKE addr, data
153
BAS.POKE16 addr, data
153
BAS.POKE8 addr, data
153
CONVERSION FUNCTIONS
154
CONVERT.DEGC_TO_F(degC)
155
CONVERT.F_TO_DEGC(degF)
155
CONVERT.TO_IEEE754(num)
155
CONVERT.FROM_IEEE754(iee754_bin)
155
CONVERT.MAP(number, fromLow, fromHigh,
toLow, toHigh)
155
BAS CONSTANTS
155
BAS.VER
156
BAS.VER$
156
BAS.ERRLINE
156
BAS.ERRNUM
156
BAS.ERRMSG$
156
BAS.FILENAME$
156
BAS.RTCMEM$
156
BAS.SSID$
156
BAS.PASSWORD$
156
BAS.LOAD
156
BAS.RESETREASON
157
OPTION COMMANDS
157
OPTION.CPUFREQ 80|160
158
OPTION.MAC mac$
158
OPTION.LOWRAM memory
158
OPTION.PWMFREQ freq
158
OPTION.NTPSYNC
158
OPTION.WDT timeout_msec
158
OPTION.WDTRESET
158
FUNCTIONS: 158
NUMERICAL FUNCTIONS
158
ABS(number) 159
ACOS(number) 159
ADC
159
APDS9960.SETUP (mode) 159
APDS9960.READGESTURE
159
APDS9960.AMBIENT
159
APDS9960.RED
159
APDS9960.GREEN
159
APDS9960.BLUE
160
APDS9960.PROXIMITY
160
APDS9960.GESTUREGAIN (gain) 160
APDS9960.GESTURELED (intensity)
160
ASC(string$) 160
ASIN(number) 160
ATAN(number) 160
ATAN2(x, y) 160
BAS.VER
160
BAS.ERRLINE
160
BAS.ERRNUM
161
BME280.SETUP(address) 161
BME280.ALT(qnh) 161
BME280.HUM
161
BME280.QFE
161
BME280.QNH(altitude) 161
BME280.TEMP
161
BNO055.SETUP( address) 161
BNO055.HEADING
161
BNO055.PITCH
161
BNO055.ROLL
161
BNO055.VECTOR ( param, axis) 162
BNO055.CALIB [(param)] 162
CINT(number) 162
CONVERT.DEGC_TO_F(degC) 163
CONVERT.F_TO_DEGC(degF) 163
CONVERT.TO_IEEE754(num) 163
CONVERT.FROM_IEEE754(ieee754_bin)
163
CONVERT.MAP(number, fromLow, fromHigh,
toLow, toHigh) 163
COS(number) 163
COUNTER.COUNT (cnt) 163
COUNTER.FREQ (cnt) 163
COUNTER.PERIOD (cnt) 163
DATEUNIX(date$) 163
DHT.TEMP
163
DHT.HUM
163
DHT.HEATINDEX
163
DISTANCE(pin_trig, pin_echo) 164
EMAIL from$, to$, subject$,
message$
164
ESPNOW.ADD_PEER(MAC_add$) 164
ESPNOW.BEGIN
164
ESPNOW.DEL_PEER(MAC_add$) 164
ESPNOW.STOP
164
ESPNOW.WRITE( msg$) 164
ESPNOW.WRITE( msg$,MAC_add$) 164
EXP(number) 164
FIX(number) 164
FILE.COPY(filename$, newfile$)
164
FILE.DELETE(filename$) 164
FILE.EXISTS(filename$) 165
FILE.RENAME(oldname$, newname$)
165
FILE.SIZE(filename$) 165
FLASHFREE
165
FUSION.ANGLE(axis) 165
INSTR([start], string$, pattern$)
165
I2C.LEN
165
I2C.READ
165
I2C.READREGBYTE (i2c_address, register)
165
I2C.END
166
INT(number) 166
LEN(string$) 166
LOG(number) 166
MILLIS
166
MQTT.Setup(server$ ) 166
MQTT.Fingerprint(fingerprint$)
166
MQTT.Connect(login$, pass$, [id$])
166
MQTT.Connect("", "", [id$]) 166
MQTT.Disconnect[()] 166
MQTT.Publish(topic$, message$)
166
MQTT.Subscribe(topic$ [,Qos])
166
MQTT.UnSubscribe(topic$) 167
MQTT.Connected[()] 167
MQTT.Status[()] 167
NEO.GETPIXEL(pos) 167
NEO.RGB(R, G, B) 167
PI 167
PID1.COMPUTE( current_value,
target_value) 167
PIN(pin_number) 167
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
169
TOUCH.X
169
TOUCH.Y
169
VAL(string$) 169
WIFI.CHANNEL
169
WIFI.MODE
169
WIFI.NETWORKS ( network$ )
169
WIFI.RSSI 169
WIFI.STATUS
170
WORD.COUNT( string$ [,delimiter$])
170
WORD.FIND( string$, find$ [,delimiter$])
170
STRING FUNCTIONS
171
BAS.ERRMSG$
172
BAS.FILENAME$
172
BAS.FTP$( host$, login$, password$,
file$, folder$) 172
BAS.PASSWORD$
172
BAS.RTCMEM$
172
BAS.SSID$
172
BAS.VER$
172
BIN$(number) 172
BUTTON$(name$, label [, id] )
172
CHECKBOX$( variable [,id]) 172
CHR$(number) 172
CSSID$(object_id, object_style)
173
DATE$[(format)] 173
ESPNOW.ERROR$
173
ESPNOW.READ$
173
ESPNOW.REMOTE$
173
FILE.DIR$[(path$)] 173
FILE.READ$(filename$,[line_num] | [start,
length]) 173
HEX$(number) 173
HtmlEventButton$
173
HtmlEventVar$
173
IMAGE$(path [,id]) 173
IMAGEBUTTON$(path, label [,id])
174
IP$
174
IR.GET$[ (param) ] 174
JSON$(string$, field$) 174
LCASE$(string$) 174
LED$(variable[$] [,id]) 175
LEFT$(string$, num) 175
LISTBOX$(variable$, "option1, option2,
option3, ..." [, height] [,id]) 175
METER$(variable, min, max [,id])
175
MID$(string$, start [,num]) 175
MAC$[ (id) ] 175
MQTT.Message$
176
MQTT.Topic$
176
OCT$(number) 176
PASSWORD$(variable [, id] ) 176
REPLACE$(expression$, find$,
replacewith$) 176
RIGHT$(string$, num) 176
RTC.DATE$[(format)] 176
RTC.TIME$
176
SERIAL.CHR$
176
SERIAL.INPUT$
176
SERIAL2.CHR$
176
SERIAL2.INPUT$
176
SLIDER$(variable, min, max [,step] [,id])
177
SPACE$(number) 177
SPI.STRING$(data$, len) 177
SPI.HEX$(datahex$, len) 177
STR$ (number [,format$ [,toint]])
178
STRING$(num, char$) 180
TEMPR$(pin_number [,ID]) 180
TEXTAREA$(variable [, id] ) 180
TEXTBOX$(variable [, id] ) 180
TRIM$(string$) 180
TIME$
180
UCASE$(string$) 180
UDP.READ$
180
UDP.REMOTE$
181
UNIXDATE$(value [,format]) 181
UNIXTIME$(value) 181
URLMSGGET$ ([arg$]) 181
WGET$( http_server$, port [,header] )
181
WGET$( url$, [,header] ) 181
WGETRESULT$
181
WPOST$(server$, body$, port [,header])
181
WPOST$(url$, body$, [,header])
182
WORD$(string$, position [,delimiter$])
182
WORD.DELETE$(string$, position
[delimiter$]) 182
WORD.EXTRACT$(string$, lead$, trail$)
182
WORD.GETPARAM$( setting$,
parameter$ [,separator$]) 182
COMMANDS: 182
AUTOREFRESH interval 183
BAS.LOAD filename$
183
BAS.RTCMEM$ = val$
183
CLS
183
CSS style_code$
183
COMMAND cmd$
183
COUNTER.RESET cnt 183
COUNTER.SETUP cnt, pin [,mode]
183
CSSEXTERNAL file$
184
DATA const1 [,const2] ... 184
DHT.SETUP pin, model 184
EMAIL.SETUP server$, port, user_name$,
password$ [, debug] 184
EMAILASYNC from$, to$, subject$,
message$
184
FILE.APPEND filename$,
content$
184
FILE.SAVE filename$, content$
184
FUSION.INIT
184
FUSION.MADGWICK ax, ay, az, gx, gy,
gz
185
FUSION.MADGWICK ax, ay, az, gx, gy, gz,
mx, my, mz
185
FUSION.MAHONY ax, ay, az, gx, gy, gz, mx,
my, mz
186
FUSION.BETA =
186
FUSION.ZETA =
186
FUSION.KI =
186
FUSION.KP =
186
HTML code$
186
I2C.SETUP sda_pin, scl_pin [,freq
[,stretch]] 186
I2C.BEGIN address
186
I2C.END
187
I2C.REQFROM address, length
187
I2C.READREGARRAY i2c_address, register,
nb_of_bytes, Array() 187
I2C.WRITE value
187
I2C.WRITEREGBYTE i2c_address,register,
value
188
I2C.WRITEREGARRAY i2c_address, register,
nb_of_bytes, Array() 188
INPUT.TIMEOUT timeout 188
INPUT["prompt$";] variable
188
INTERRUPT pin_no, {OFF | label}
188
IR.INIT pin_rx | OFF [, pin_tx]
189
IR.SEND type, code$, bits
189
JSCALL javaCode$
189
JSCRIPT script$
189
JSEXTERNAL file$
189
LCD.INIT address, cols, rows
189
LCD.CLS
189
LCD.CUSTOM char, array() 189
LCD.PRINT x, y, text$
189
LCD.WRITE char 190
LOCAL var1 [,var2], ... 190
MAXDISPLAY.SETUP CS_pin
190
MAXDISPLAY.PRINT msg$ [,‘brightness]
190
MAXSCROLL.SETUP nb_devices, CS_pin
[,reverse] 190
MAXSCROLL.PRINT msg$
190
MAXSCROLL.NEXT msg$
190
MAXSCROLL.SHOW pos [, brightness]
190
MAXSCROLL.SCROLL [brightness]
190
MAXSCROLL.OSCILLATE [brightness]
191
NEO.PIXEL led_pos, R, G, B [, disable]
191
NEO.PIXEL led_pos, COLOR [, disable]
191
NEO.SETUP [nb_led] 191
NEO.STRIP led_start_pos, led_end_pos, R,
G, B [, disable] 191
NEO.STRIP led_start_pos, led_end_pos,
COLOR [, disable] 191
NEOSCROLL.SETUP nb_devices, pin
[,serpentine] 191
NEOSCROLL.PRINT msg$
191
NEOSCROLL.NEXT msg$
191
NEOSCROLL.COLORS col$
191
NEOSCROLL. NEXTCOLORS col$
192
NEOSCROLL.SHOW pos [, brightness]
192
NEOSCROLL.TEXT msg$
192
NEOSCROLL.SCROLL [‘brightness]
192
NEOSCROLL.OSCILLATE [‘brightness]
192
OLED.CLS
192
OLED.INIT orientation [,model]
192
OLED.REFRESH fmt 192
OLED.COLOR color 193
OLED.PIXEL x, y
193
OLED.LINE x1, y1, x2, y2
193
OLED.RECT x,y, width, height [,fill]
193
OLED.CIRCLE x, y, radius [, fill]
193
OLED.FONT font_num
193
OLED.PRINT x, y, text$ [background]
193
OLED.IMAGE x, y, image$
193
ONERROR ABORT or ONERROR IGNORE or
ONERROR SKIP [nn] or ONERROR CLEAR or ONERROR GOTO label
194
ONESPNOWERROR [label | OFF] 194
ONESPNOWMSG [label | OFF] 194
ONGESTURE [label | OFF] 194
ONHTMLCHANGE [label | OFF] 194
ONHTMLRELOAD [label | OFF] 194
ONINFRARED label 194
OnMQTT label 194
ONSERIAL [label | OFF] 194
ONSERIAL2 [label | OFF] 194
ONTOUCH [label | OFF] 194
ONUDP [label | OFF] 195
ONURLMESSAGE [label | OFF] 195
ONWGETASYNC [label | OFF] 195
OPTION.CPUFREQ 80|160
195
OPTION.MAC mac$
195
OPTION.LOWRAM value
195
OPTION.PWMFREQ value
195
OPTION.NTPSYNC
195
OPTION.WDT timeout_msec
195
OPTION.WDTRESET
195
PAUSE delay
195
PCA9685.SETUP addr [,freq] 195
PCA9685.SETFREQ freq
196
PCA9685.PWM pin, value
196
PID1.INIT Kp, Ki, Kd
196
PID1.LIMITS min, max
196
PID1.PERIOD msec
196
PID1.PARAMS Kp, Ki, Kd
196
PID1.SETMODE mode
196
PIN(pin_number) = val 196
PIN.MODE pin_number, mode [,PULLUP]
196
PIN.TONE pin, freq [,duration]
197
PRINT expression[[,; ]expression] ...
197
PRINT2 expression [[,; ]expression] ...
197
PWM(pin_number) = value
197
READ var1 [,var2] ... 197
REBOOT
197
REFRESH
197
RESTORE
197
RTC.SETTIME Year, Month, Day, Hours,
Minutes, Seconds
198
SERIAL.BYTE ch1 [,ch2] . . . 198
SERIAL2.BYTE ch1 [,ch2] . . .
198
SERIAL.MODE baudrate [, bits, parity,
stop] 198
SERIAL2.MODE baudrate, pin_tx, pin
rx [, bits, parity, stop] 198
SERVO id, value
198
SERVO.SETUP id, pin_number |
OFF
198
SETTIME Year, Month, Day, Hours, Minutes,
Seconds
198
SLEEP value
199
SPI.SETUP speed [,data_mode [,
bit_order]] 199
SPI.SETMODE data_mode
199
SPI.SETFREQ speed
199
ST7920.INIT CS_pin
199
ST7920.CLS
199
ST7920.REFRESH fmt 199
ST7920.COLOR color 199
ST7920.PIXEL x, y
200
ST7920.LINE x1, y1, x2, y2
200
ST7920.RECT x,y, width, height [,fill]
200
ST7920.CIRCLE x, y, radius [, fill]
200
ST7920.FONT font_num
200
ST7920.PRINT x, y, text$ [background]
200
ST7920.IMAGE x, y, image$
200
TM1637.PRINT msg$ [, brightness]
200
TM1637.SETUP data_pin, clock_pin [,
bit_delay] [, display_type]] 200
TM1638.PRINT msg$ [, brightness ]
200
TM1638.SETUP data_pin, clock_pin,
strobe_pin
201
TM1638.LEDS val 201
TFT.BMP filename$, [x, y [, back_color] ]
201
TFT.CIRCLE x, y, radius [, fill]
201
TFT.FILL color 201
TFT.INIT CS_pin, DC_pin, orientation
[Display_width, Display_height, Variant] 201
TFT.LINE x1, y1, x2, y2, col 202
TFT.PRINT expression [[,; ]expression]
... 202
TFT.RECT x, y, width, height, color [
[,fill] ,[round_radius] ] 202
TFT.TEXT.COL color [,backcolor]
202
TFT.TEXT.POS x, y
202
TFT.TEXT.SIZE size
202
TIMER0 interval, label 202
TIMER1 interval, label 202
TOUCH.SETUP T_CS_pin
202
TRACE message
203
UDP.BEGIN(port) 203
UDP.REPLY msg$
203
UDP.STOP
203
UDP.WRITE ip, port, msg$
203
URLMSGRETURN msg$ [,content_type$]
203
WAIT
203
WGETASYNC [(] server$, port, [,header
[,ping]] [)] 204
WGETASYNC [(] url$, [,header [,ping]] [)]
204
WIFI.APMODE SSID$, password$ [, channel]
[, IP$ , MASK$] 204
WIFI.AWAKE
204
WIFI.CONNECT SSID$, password$ [, BSSID$]
[, IP$ , MASK$ [, GATEWAY$]] 205
WIFI.POWER pow
205
WIFI.SCAN
205
WIFI.SLEEP
205
WLOG expression[[,; ]expression] ...
205
WORD.DELPARAM setting$, parameter$,
[,separator$] 206
WORD.SETPARAM setting$, parameter$,
value$ [,separator$] 206
BASIC KEYWORDS
207
CASE
208
DIM array(size) [, …] [= init1, init2, …]
208
DO
208
ELSE
208
END [IF | SELECT | SUB] 208
ENDIF
208
EXIT {DO | FOR | SUB} 208
FOR
208
GOSUB [label | lab$] 208
GOTO [label | lab$] 208
IF
208
LET var = expression
208
LOOP
208
NEXT
208
OFF
208
OUTPUT
208
PULLUP
209
REM
209
RETURN
209
SELECT
209
SPECIAL
209
STEP
209
SUB
209
THEN
209
TO
209
UNTIL
209
WEND
209
WHILE
209
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 and 6 digits 7-segments display
-
TM1638 8 digits 7-segments display including 8 leds and 8
buttons
-
MAX7219 8 digits 7-segments display
-
MAX7219 8x8 dot matrix display modules
-
Neopixel WS2812 led strips
-
Neopixel WS2812 8x8 dot matrix display
-
PCA9685 PWM/SERVO module
-
Infrared interface with many RC protocols (transmission and
reception)
-
RTC module (DS1307 or DS3231)
-
HC-SR04 ultrasonic sensor for distance measurement
-
BNO055 Absolute Orientation Sensor
-
BME280 Combined humidity and pressure sensor
-
APDS9960 Digital Proximity, Ambient Light, RGB and Gesture
Sensor
-
RFID MFRC522 cards reader
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 should not be named the same as
a command name, and must follow the same format as variables (see
below).
A branch label definition must begin the line,
and a colon (":") must terminate the label definition.
Any references to the defined label (GOTOs and
GOSUBs etc) do not use a colon.
Example :
b
=
10
a
=
20 : c
=
30
GOSUB
LABEL1
END
LABEL1:
print
"Label1"
RETURN
|
The interpreter has 2 types of variables:
-
Floating Point (double precision)
-
String
Floating point variables can store numbers
with decimal 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 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"
Variables and arrays defined in the main code
are global, therefore any variable is accessible from any part of
the code after it has been previously defined there.
Variables and arrays defined inside
“user defined” subroutine (SUB) are visible only inside that sub
and inside all the code called by that subroutine; their content
(and their memory space) is removed at the end of the SUB
The LOCAL command permits defining local
variables inside of "user defined" subroutines; this permits to use
the same name of an “already existing” variable locally without
modifying the original.
As for all the variables defined inside SUB,
they will disappear at the end of the subroutine.
Example:
A
=
10
B
=
20
C
=
30
mysub
"Hello"
PRINT
A,B, C
END
SUB
mysub(a$)
LOCAL
A,B
A
=
123
B
=
456
C
=
789
D
=
8888
PRINT
A$, D
END
SUB
|
In this example, calling the user-defined
subroutine "mysub" will not modify the content of the global
variables A and B (defined locally) but will modify the content of
the variable C (not defined locally) and the variable D will
disappear at the end of the SUB.
The keywords recognized by the interpreter can
be defined into 3 classes:
●
Operators
●
Commands
●
Functions
The Operators are symbols that 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.
The following operators are available. These
are listed in the following tables by order of precedence.
Operators on the same line are processed with a left to right
precedence.
Arithmetic operators:
^
|
Power
|
* / \ MOD
|
Multiplication,
division, integer division and modulo (remainder of the
division)
|
+ -
|
Addition and
subtraction
|
Shift operators:
x <<
y
x >> y
|
These operate in a
special way. << means that the value returned will be the
value of x shifted by y bits to the left while >> means the
same only right shifted. They are integer functions and any bits
shifted off are discarded and any bits introduced are set to
zero.
For more
information about the kinds of bitwise shifts, see Bitwise shifts.
|
Logical operators:
<>
< > <=
>= =
|
Not Equal, less
than, greater than, less than or equal to,
greater than or
equal to, equal
|
AND OR NOT XOR
|
Conjunction,
disjunction, negation, Exclusive OR
|
String operators:
<> <
> <=
>=
=
|
Not Equal, less
than, greater than, less than or equal to,
greater than or
equal to, equal
|
+ &
|
Add strings
together
|
Bitwise operators:
AND OR XOR NOT
|
Binary AND, binary
OR, binary exclusive OR, binary negation
For more
information about the bitwise operators, see
Bitwise
Operators
|
The operators AND, OR and XOR are integer
bitwise operators. For example PRINT (3 AND 6) will output 2.
Expressions beginning with open parenthesis
‘(‘ are always considered numerical but the parser is able to
determine if an expression is true or false even if the expression
represents a string.
Each expression representing a comparison,
returns a numerical value of 1 if the expression is true or 0 if
false.
For example 10 = 10 represents a value of 1
whereas 10 = 5
represents a value of 0.
The same logic is applied for string
expressions where "abc" =
"abc" represents a value of 1 and "abc" = "def" represents a value
of 0.
This is very useful in the IF command and also
in other expressions.
For example the following code :
A$
=
"on"
If
A$
=
"on"
then
pin(4) = 1
Else
pin(4) = 0
End
if
|
Can be replaced by
pin(4)
= (a$ =
"on")
The 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"
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
The WHILE
WEND loop can have the following
syntax :
WHILE
expression
Statements
WEND
The loop is iterated as long as the expression
is true
Example:
i
= 0
WHILE
i < 3
Print i
i = i +
1
WEND
Will print 0, 1, 2
The DOLOOP
can have one of the following 4 syntax :
DO
WHILE expression
Statements
LOOP
DO
UNTIL expression
Statements
LOOP
DO
Statements
LOOP
WHILE expression
DO
Statements
LOOP
UNTIL expression
The command
EXIT
DO can be used to exit from the
loop at any time
Example
i =
0
DO
Print
i
i = i +
0.5
LOOP
UNTIL i >3
Will print 0, 0.5, 1, 1.5,
2, 2.5, 3
i =
0
DO
Print
i
i = i +
0.5
IF
i > 2 THEN
EXIT DO
LOOP
UNTIL i >3
Will print 0, 0.5,
1, 1.5, 2
The SELECTcan
have the following syntax:
SELECT
CASE expression
CASE exp1 [,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
The GOTOcan have
the following syntax :
GOTO
[LABEL | LAB$]
Example
a =
5
IF a
> 5 THEN GOTO
LABEL1
END
....
LABEL1:
PRINT
"This is label1"
....
The goto must be
considered as an obsolete command and is provided just for backward
compatibility with old style Basic programs.
The GOSUBcan have
the following syntax :
GOSUB
[LABEL | LAB$]
The called function
must terminate with the command RETURN
Example
a = 5
IF a
> 5 THEN GOSUB LABEL1
END
....
LABEL1:
PRINT
"This is label1"
RETURN
The command DATA is used to store constant
information in the program code, and is associated with the command
READ. Each
DATA-line can contain one or more constants separated by commas.
Expressions containing variables will be also evaluated
here.
The goal of the DATA is to avoid repetitive
variable assignation lines, in particular for arrays.
The DATA values will be read from left to
right, beginning with the first line containing a DATA statement.
Each time a READ instruction is executed the saved DATA position of
the last READ is advanced to the next value. Strings must be
written in quotes like string constants. The command RESTORE resets
the pointer of the current DATA position, so the next READ will
read from the first DATA found from the beginning of the
program.
In case READ uses the wrong variable type
the error message "Type mismatch" appears while referring to
the line number containing the READ statement that triggered the
condition.
DATA lines may be scattered throughout the
whole program code, but for the sake of clarity they would be
better kept together at the beginning of the program.
The DATA can have
the following syntax :
DATA const1
[,const2] …..
The constants can
be Numerical or String.
Example :
DATA 1, 55.88,
"constant", 99
READ A, B, C$,
D
PRINT A, B, C$,
D
Example without
DATA:
dim colors$(5)
colors$(1)
= "Red"
colors$(2)
= "Green"
colors$(3)
= "Blue"
colors$(4)
= "Yellow"
colors$(5)
= "Magenta"
Same example but
using DATA:
DATA
"Red", "Green",
"Blue", "Yellow", "Magenta"
dim colors$(5)
For i=1
to 5
Read colors$(i)
Next i
Define the end of
the program. With this command the program stops.
It can also be
:
END IF -> close the IF command
END SELECT -> closes the SELECT CASE command
END SUB -> closes the user defined SUB
Permit to exit from
a loop or a user defined SUB.
The syntax is :
EXIT
DO -> exit from a DO loop
EXIT
FOR
-> exit from a FOR loop
EXIT
SUB
-> exit from a user defined SUB.
Define a
user-defined subroutine, which the script can use like a command or
function.
User-defined
subroutines are effectively additional commands, so cannot be used
as branch labels.
Permit to create a
user defined command with optional parameters.
The syntax is SUB
subname[(arg1 [,arg2] …)]
The variables are
passed by reference; this means that the arguments, if modified
inside the subroutine, will modify the original variable. This can
be useful to return values from the subroutine (acting like a
function).
It is possible to
pass arrays using the syntax array_name().
Using the
LOCAL command will permit to define local variables (useful
to avoid to modify existing global variables).
Example 1 :
routine cube
SUB
cube(x)
PRINT
X
^3
END
SUB
cube
3 ' will print
27
|
Example 2:
routine cube with returning argument
SUB
cube(x,y)
y =
x
^3
' the value
is returned using the 2nd argument
END
SUB
ret
=
0
cube 5,
ret
PRINT
ret
' will print
125
|
Example 3:
routine with local variables and returning argument
SUB
left_trim(s$,
ret$)
LOCAL
i
i =
1
DO
UNTIL
i
=
len(s$)
IF
mid$(s$,
i, 1)
<>
" "
THEN
EXIT
DO
i =
i
+
1
LOOP
ret$ =
mid$(s$,
i)
END
SUB
z$ =
""
FOR
i
=
1
to
3
left_trim
" remove space from left ",
z$
PRINT
z$
+
"--"
NEXT
i
|
Will print
remove space from
left
--
remove space from
left
--
remove space from
left
--
As you can see in
this example, the variable i in the FOR loop is not modified by the
LOCAL variable i in the subroutine.
Example 4:
pass arrays
SUB
pass_array(f(),
c$())
Dim
myArray(10)
myArray(0)
=
456
Print
f(0),
c$(0),
myArray(0)
f(1)
=
123
c$(1)
=
"myText"
END
SUB
Dim
alpha(10)
Dim
beta$(10)
alpha(0)
=
456
beta$(0)
=
"testme"
Pass_array alpha(),
beta$()
Print
alpha(1),
beta$(1)
|
In this example,
the array alfa() is passed locally to the array f()
and the array beta$() is passed locally to the array
c$().
Modifying locally
these arrays change the value of the original one as their content
is passed by reference.
The array
“myArray” will disappear at the end of the SUB
As the numerical
variables are stored internally as double precision floating
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
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
|
|
Displays the error message and abort the
program.
This is the normal behaviour and is the
default when a program starts running.
|
|
Any error will be simply ignored.
As this can make very difficult to debug a
program it should be used wisely.
|
|
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.
|
|
Reset the eventual pending error
|
ONERROR
GOTO [label
| OFF]
|
Jumps to the error handling routine defined by
the label.
It can be removed (hence reverting to ONERROR
ABORT) replacing the label with OFF.
Using RETURN inside the error handling routine
will continue the execution on the line following the error.
|
When an error occurs, the following constants
are available :
CONSTANT
|
DESCRIPTION
|
|
Returns the line
number where the error happened. Value of 0 means no error.
It is reset to 0
with the command ONERROR CLEAR or running the program or with
the command ONERROR IGNORE or ONERROR SKIP.
|
|
Returns a number
where non zero means that there was an error.
It is reset to 0
with the command ONERROR CLEAR or running the program or with
the command ONERROR IGNORE or ONERROR SKIP.
|
|
Return a string
representing the error message that would have normally been
displayed on the console. It is reset to “No Error” running the
program or with the command ONERROR CLEAR or ONERROR IGNORE or
ONERROR SKIP.
|
Example of error handling using the command
ONERROR GOTO :
ONERROR
GOTO
Error_Handler
Print
"start"
Print
3/0
' this
generates a divide by zero error
Print
space$(60000)
' this
generates an out of memory error
End
Error_Handler:
Print
"Error text
";
BAS.ErrMsg$
Print
"Error
num ";
BAS.ErrNum
Print
"Error line
";
BAS.ErrLine
Return
' returns to
the line following the error
|
When a client
connects to the module using its IP address, the module will
redirect automatically to the url ‘/output?menu’, which sends an
empty html page present on the module.
That page contains
a bunch of javascript code permitting to interface the page with
the module using javascript.
This page will
automatically open a websocket connection with the module; the
"squared led" indicates if the connection was successful (green) or
not (red).
A mechanism of ping
- pong has been implemented into the javascript in order to hold
the connection alive all the time. If the connection is lost, the
page will try to reconnect automatically without any manual
action.
The button
"reconnect" 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.
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.
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.
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.
This is a special event that happens every
time aBUTTON$
object is clicked in the HTML pages.
When this happens,
a special variable
HtmlEventButton$ is created containing the name of the
button that was clicked.
This is useful to
determine the button within a group of buttons.
Let’s see an
example:
CLS
HTML
Button$("ON",
buttonEvent)
+
"
" +
Button$("OFF",
buttonEvent)
wait
buttonEvent:
print
"You clicked
on ";
HtmlEventButton$
return
|
This event is triggered when an object present
in the HTML output page changes its value.
It is useful to make actions when something
changes in the HTML Pages.
When this event
happens, a special variable
HtmlEventVar$ is created containing the name of the
variable that changed its value.
This is useful to
determine the object that generated the event.
Let’s see an example :
CLS
text$
=
"Change me,
please"
HTML
TEXTBOX$(text$)
OnHtmlChange
Jump1
'will jump to
Jump1 when a variable changes on the web page
Wait
'pause
waiting for the event
Jump1:
Print
text$
'print the
content of the variable inside the terminal console
Return
|
This event is triggered when a Websocket
connection request is accepted.
This can be used to automatically fill the
content of the WEB page each time a client connects to the
module.
Let’s see an example :
CLS
OnHtmlReload
Fill_Page
'will jump to
Fill_Page when the page is reloaded
gosub
Fill_Page
'load the
page for the first time
Wait
'pause
waiting for the event
Fill_Page:
'place where
the page begins to be created
CLS
a$
=
"Now style
me, please<br>"
a$
=
a$
+
"Button1:
<button id='but1' type='button'>ON</button>
"
a$
=
a$
+
"Button2:
<button id='but2'
type='button'>OFF</button>"
HTML
a$
a$
=
cssid$("but1",
"background-color:
red;")
a$
=
a$
+
cssid$("but2",
"background-color:
green;")
HTML
a$
Return
|
This event is
triggered when a code is received by the infrared receiver.
Refer to chapter
INFRARED
INTERFACE for more details.
This event is
triggered when a message is received on the serial port.
Example:
print
"Ram Available ";
ramfree
onserial
rec1
wait
rec1:
'print serial.input$
print
serial.chr$;
return
|
This event is
triggered when a message is received on the serial port #2.
Example
serial2.mode
9600, 2, 5
' set serial port #2 to 9600 pin 2 TX, pin 5
RX
print2
"Ram Available ";
ramfree
onserial2
rec2
wait
rec2:
print serial2.input$
return
|
This event is
triggered when the TFT screen is touched.
Refer to the
chapter TouchScreen for more details.
This event is
triggered when a UDP message is received.
Example:
udp.begin
5001
'set the UDP commmunication using port
5001
onudp
goudp
'Write several messages to the port
for i
=
0 to 100
udp.write
"192.168.1.44",
5001,
"Hello "
+
str$(i)
next i
wait
goudp:
v$
=
udp.read$
'receive the UDP data
print
v$
return
|
This event is
triggered when a WgetAsync message is received.
This is associated
with the command
WGETASYNC.
The goal of the
WGETASYNC command is to start a html get request without
the module having to wait for the answer.
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
|
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
This event is
triggered when a ESP-NOW message is received.
Example:
espnow.begin
' init the ESP-NOW
onEspNowMsg message
' set the place where jump in case of message
reception
wait
message:
print "Message
Received!"
return
|
This event is
triggered when a ESP-NOW 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
|
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
|
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.
If a program is defined to run automatically
(“Autorun File” in the config page), the WiFi connection process is
slightly different.
If the option “Fast boot” in the config page
is selected, the program will be executed immediately and the WiFi
will be powered ON after a little delay ( 0.1 sec ).
If the command
WIFI.SLEEP is executed during the very beginning of the
program ( for example as the first line of the program) the WiFi
will be simply disabled without using any power.
This enhances the use of the module in low
power applications (i.e. on battery).
The WiFi connection can then be restored later
using the commands
WIFI.CONNECT or
WIFI.APMODE.
If the command WIFI.SLEEP is not executed at
the beginning of the program, the WiFi connection will be
established by default as described in the previous chapter
(WiFI CONNECTIONS).
The function
BAS.RESETREASON can be used at the beginning of the
program to understand the reasons for the restart of the module
enabling it to take the appropriate actions.
In 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.
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
|
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.
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
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
|
|
Copy the file filename$
into the file newfile$
Returns 1 in case of success or
0 if error
|
|
Delete the file specified by filename$
Returns 1 in case of success or
0 if error
|
|
Returns 1 if filename$
exists, otherwise returns 0
|
|
Rename the file oldname$
to newname$
Returns 1 in case of success or
0 if error
|
|
Returns the size of the file (in bytes) if the
file exist, otherwise returns -1
|
|
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.
|
|
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.
|
|
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")
|
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)
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
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
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)
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)
a =IObuff.bit(buff_num,
position, bit)
returns the value of the bit of the byte at
the position of the buff_num
returns 0 or 1
IObuff.setbit(buff_num,
position, bit)
set the bit of the byte at the position
of the buff_num
IObuff.clearbit(buff_num,
position, bit)
clear the bit of the byte at the
position of the buff_num
IObuff.togglebit(buff_num,
position, bit)
toggle the bit of the byte at the
position of the buff_num
IObuff.copy(dest_buff_num
[,pos])
, (source_buff_num, [, start [, size]])
Copy the from source_buff to dest_buff
pos is the
position where the source must be copied in the source (0 by
default)
start and
size define what must be copied (have the same meaning as
in
.WRITE_IOBUFF)
UDP - use the remote controller APP for IOS
devices (iphone and Ipad)
' I/O buffers example using the RCWController
' available in the IOS app store.
' It uses by default the port 10000
' The APP sends a block of 10 bytes that
' will be printed in the console on the same line
udp.begin
10000
' define the place where jump on message reception
onudp
received
wait
received:
' read the incoming data in the buffer 0
udp.read_iobuff(0)
size
=
iobuff.len(0)
print
"received ";
size;
" bytes"
for
z =
0
to
9
' read and print 1 byte at the time on the same line
print
iobuff.read(0,
z),
next
z
Print
' print an empty line
return
|
File read and transfer to the serial port
by blocks
' I/O BUFFERS example using files
' read a file in blocks of 512 characters
' and send them to the serial port (print)
fileName$ =
"/data8.txt"
block_size =
512
' size of the block to be read
file_size =
file.size(fileName$)
print
"File size ";
file_size
print
file_size
for
z =
0
to
file_size -
1
step
block_size
file.read_iobuff(0),
fileName$, z, block_size
' send the block on the serial port (print)
serial.write_iobuff(0)
next
|
Serial port data logger
' I/O BUFFERS example to create a serial data logger
' receive bytes from the serial port and
' write them into the file /mylog.txt
' all the characters will be recorded
' including the ASCII 0 (NUL)
filename$ =
"/mylog.txt"
' define the place where jump on message reception
onserial
received
wait
received:
' waits for 10 millisec giving time to receive all the
data
pause
10
' read the incoming data in the buffer 0
serial.read_iobuff(0)
size
=
iobuff.len(0)
print
"received ";
size;
" bytes"
' appends the received data to the file
file.append_iobuff(0),
filename$
return
|
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)
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
|
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.
The ESP8266 contains several H/W interfaces
that can be controlled by Annex WI-Fi Basic using specific commands
and functions.
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
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
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
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
|
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
|
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
|
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
|
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
|
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)
This is an example of connection of a module
with 74HC595 bought on Ebay at less than 2€
This drawing shows
how this module must be connected to the ESP8266.
It provides 8
digital outputs.
This is an example
of code that "drives" this module:
'Write from 0
to 255 on the module
'Each pin
will blink at different frequency
spi.setup
100000
' set the SPI
port at 100KHz
pin.mode
16,output
' set the Pin
used for CS as output
for
i
=
0
to
255
M_74HC595 i
next
i
end
sub
M_74HC595(x)
pin(16)
=
1
'put the CS
HIGH
r =
spi.byte(x)
pin(16)
=
0
'pulse the CS
low then high
pin(16)
=
1
end
sub
|
This is another example of a connection of a
module with MCP23S17 bought on Ebay at less than 2€.
This module provides 16 GPIO pins that can be
used as digital input or output.
As this device is quite simple to interface,
it can be directly driven using a “driver” written in basic.
This is an example using the SPI pins and the
GPIO15 as CS signal
' MCP23S17 Driver for Annex
' datasheet
http://ww1.microchip.com/downloads/en/DeviceDoc/20001952C.pdf
pin(15)
=
1
'the GPIO15 is the CS
pin.mode
15,
output
spi.setup
1000000
'MCP23S17 SPI address
MCP23S17_ADDR =
&h40
' assumes A2, A1, A0 to GND
'MCP23S17 internal registers
IODIRA =
0 : IODIRB
=
1 : IPOLA =
2 : IPOLB
=
3
GPINTENA =
4 : GPINTENB
=
5 : DEFVALA =
6 : DEFVALB
=
7
INTCONA =
8 : INTCONB
=
9 : IOCONA =
10 : IOCONB
=
11
GPPUA =
12 : GPPUB
=
13 : INTFA =
14 : INTFB
=
15
INTCAPA =
16 : INTCAPB
=
17 : GPIOA =
18 : GPIOB
=
19
OLATA =
20 : OLATB
=
21
MCP23S17_WRITE IOCONA, &h08
' init MCP23S17 with bit HAEN
MCP23S17_WRITE IODIRA, &h00
' all PORT A pins as output
MCP23S17_WRITE IODIRB, &hff
' all PORT B pins as input
MCP23S17_WRITE GPPUB , &hff
' all PORT B pins as pullup
v =
0
for
i
=
0
to
255
for
z
=
0
to
255
MCP23S17_WRITE GPIOA, z
' pulse all GPIOA pins
MCP23S17_READ GPIOB, v
' read all GPIOB pins
print
v
next
z
next
i
End
' function for read / write the MCP23S17
sub
MCP23S17_WRITE(register,
value)
local
a
pin(15)
=
0
a =
SPI.byte(MCP23S17_ADDR)
a =
SPI.byte(register)
a =
SPI.byte(value)
pin(15)
=
1
end
sub
sub
MCP23S17_READ(register,
value)
local
a
pin(15)
=
0
a
=
SPI.byte(MCP23S17_ADDR
or
1)
a
=
SPI.byte(register)
value
=
SPI.byte(0)
pin(15)
=
1
end
sub
|
An LCD display can be connected to the module
using I2C interface.
These displays are very cheap and available on
Ebay at less than 4€.
This picture shows an LCD with 4 lines at 20
characters per line.
In general these displays are based on the
chip HD44780 and works with a parallel interface.
Because the number of pins available on the
ESP module is very limited, there is an additional module (in
general sold with the display itself) permitting the connection
using the bus I2C.
This picture shows the module (generally
soldered in the back of the display) enabling the I2C
connection.
These modules are based on the chip PCF8574
and have the following relationship between the display pins and
the bits of the PCF8574:
PCF8574 BIT
|
LCD SIGNAL
|
|
PCF8574 BIT
|
LCD SIGNAL
|
BIT
0
|
RS
|
|
BIT 4
|
D4
|
BIT
1
|
RW
|
|
BIT 5
|
D5
|
BIT
2
|
E
|
|
BIT 6
|
D6
|
BIT
3
|
BACKLIGHT
|
|
BIT 7
|
D7
|
However, this mapping is managed directly into
the ESP module so you don’t need to worry about it.
The only important information is the I2C
address of the display which may change depending on the card.
The connection is very simple, just 2 pins for
the I2C and the power supply are required.
An important point is that the display must be
powered with 5V because it will not work at 3.3 V.
In order to use the LCD, there are 2 steps
:
-
Initialise the I2C bus
-
Init the display
This can be done with the following commands
:
I2C.SETUP 4,
5 ' set I2C port on pins 4 and 5
LCD.INIT 63, 20,
4 ‘ init an LCD at address 63 (3F in hex) with 20
characters per line and 4 lines
After these 2 lines, there are 2 additional
commands available :
LCD.CLS ‘ clear the screen of the LCD
LCD.PRINT x, y,
text$ ‘ print a text on the LCD at the position
(x, y)
Example:
I2C.SETUP
4,
5 'set I2C port
on pins 4 and 5
'init an LCD
at address 63 (3F in hex) with 20 characters per line and 4
lines
LCD.INIT
63, 20,
4
LCD.CLS
' clear the
screen of the LCD
'print a
message on the LCD at the first char of the first
line
LCD.PRINT
1,
1, "HELLO
WORLD"
|
In addition it is possible to control the
backlight of the display using the functions
LCD.OFF
' turns OFF the backlight of the
LCD
LCD.ON '
turns ON the backlight of the
LCD
The LCD has the capability to hold 8 custom
characters identified as the ASCII chars from 0 to 7.
The command
LCD.CUSTOM char,
array() enables to define these custom characters.
For example, to define the custom char 2 :
dim a(8)
= 0, 0, 10, 21, 17, 10, 4, 0
'these are 8 bytes defining the 8
rows
LCD.CUSTOM 2,
a() 'set the character 2
LCD.PRINT 1,1,
CHR$(2)
' print the char
Finally the command
LCD.WRITE char
enables to print a single char;
It enables, in particular, to print the
character 0 (that is ignored in
LCD.PRINT).
For example, this program :
I2C.SETUP
4, 5
'set I2C port on pins 4 and 5
'init an LCD at address 63 (3F in hex) with 20 characters per line
and 4 lines
LCD.INIT
63, 20, 4
LCD.CLS
' clear the screen of the LCD
'print a message on the LCD at the first char of the first
line
LCD.PRINT
1, 1,
"HELLO WORLD"
'Create 8 custom chars
dim
a(8)
=
0, 0, 10, 21, 17, 10, 4, 0
'Heart
LCD.CUSTOM
0, a()
dim
a(8)
=
0, 0, 10, 31, 31, 14, 4, 0
'Heart filled
LCD.CUSTOM
1, a()
dim
a(8)
=
0, 10, 0, 0, 17, 14, 0, 0
'smile
LCD.CUSTOM
2, a()
dim
a(8)
=
0, 10, 0, 0, 14, 17, 0, 0
'sad
LCD.CUSTOM
3, a()
dim
a(8)
=
0, 14, 17, 17, 17, 10, 10, 27
'omega
LCD.CUSTOM
4, a()
dim
a(8)
=
4, 14, 31, 4, 4, 4, 4, 4
'arrow up
LCD.CUSTOM
5, a()
dim
a(8)
=
4, 4, 4, 4, 4, 31, 14, 4
'arrow down
LCD.CUSTOM
6, a()
dim
a(8)
=
0, 4, 10, 17, 10, 4, 0, 0
'diamond
LCD.CUSTOM
7, a()
LCD.print
1 ,2,
""
'set the cursor on the 2nd line
'Print the 8 custom chars
for
z =
0
to
7
LCD.WRITE
z
next
z
|
Will give this result on the LCD:
The custom characters can be created online
using this website https://maxpromer.github.io/LCD-Character-Creator/
For example, the char defined in the image,
can be defined in annex as below :
dim
a(8)
=
&h04, &h0E, &h1F, &h04, &h04, &h1F
&h0E, &h04
LCD.CUSTOM
3, a()
|
An OLED display can be connected to the module
using the I2C interface.
These displays are very cheap and available on
Ebay at less than 3€.
This picture shows an OLED with 128 x 64
pixels monochrome but the size is only 0.96".
This display is based on the chipset
SSD1306
It is also possible to use a display based
on the chipset SH1106.
The connection is very simple, just 2 pins for
the I2C and the power supply are required.
In order to use the OLED, there are 2 steps
:
-
Initialise the I2C bus
-
Init the display
This can be done with the following commands
:
I2C.SETUP 4,
5 ' set I2C port on pins 4 and
5
OLED.INIT orientation
'init with a
given orientation (0 = normal, 1 = upside-down)
In case of the display SH1106, the command
is
OLED.INIT orientation,
1 'init with a given orientation
(0 = normal, 1 = upside-down)
After these 2 lines, there are several
commands available :
OLED.CLS, OLED.COLOR, OLED.FONT, OLED.PIXEL, OLED.LINE, OLED.RECT,
OLED.CIRCLE, OLED.PRINT, OLED.IMAGE, OLED.REFRESH
The current implementation of the OLED is
based on a double buffering; this permit to draw in background on
the screen while the current image is still shown. This technique
permit to avoid flickering while drawing objects on the screen. The
command
OLED.REFRESH fmt
permit to choose between an automatic refresh
(OLED.REFRESH
1)
or a manual refresh (OLED.REFRESH
0).
By default the refresh is automatic.
When an automatic refresh is set, the image is
immediately updated after each drawing command whereas, with the
manual refresh, the image is refreshed only when an
OLED.REFRESH command is executed.
The
OLED.COLOR col
defines the color to be used by the different drawing commands. As
the display is monochrome, only the color 0 (black) and 1(white)
can be defined; an additional color 2 (reverse) permit to draw
object that reverse the existing color already present on the
screen; useful to draw and clear the same object. By default the
color is 1 (white).
The
OLED.IMAGE x, y,
image$ permit to draw an image on the screen from a
file. The file format must be XBM, a kind of ‘C’ source code. This
format is not really popular but it is supported by the free tool
Gimp.
The command
OLED.FONT font_num
permits to define the font to be used by the command
OLED.PRINT.
There are 4 fonts available, FIXED_5X7, ARIAL
MT10, ARIAL MT16, ARIAL MT24.
Example:
I2C.SETUP
4,
5 ' set I2C
port on pins 4 and 5
OLED.INIT
1
' init the
OLED upside-down
OLED.CLS
' clear the
screen
OLED.FONT
2
OLED.COLOR
1
OLED.PRINT
10,10, "HELLO
WORLD"
|
An ST7920 LCD display can be connected to the
module using the SPI interface.
These displays are very cheap and available on
Ebay at less than 5€.
This picture shows an ST7920 with 128 x 64
pixel monochrome.
Some cheap boards will have PSB
connected to VDD (5v) This forces the parallel interface to be
used. Before you connect your display check that pin 2 (VDD ,5v)
and pin 15 (PSB) are not connected. If they are you may
need to cut a jumper. Otherwise you will short out your power
supply, and the display will not work.
This display is provided with a parallel
interface BUT it can be used with a SPI interface, so only 3 pins
are required.
As this display use the SPI bus and its speed
it is limited at around 1Mb/sec the first command must be:
SPI.INIT 1000000
Then, in order to use the display, it must be
initialised.
This can be done with the following
command:
ST7920.INIT CS_pin
As per the wiring above, the command is
ST7920.INIT
15
After these 2
lines, there are several commands available :
ST7920.CLS,
ST7920.COLOR, ST7920.FONT, ST7920.PIXEL, ST7920.LINE, ST7920.RECT, ST7920.CIRCLE, ST7920.PRINT, ST7920.IMAGE, ST7920.REFRESH
The current implementation of the ST7920 is
based on a double buffering; this permits drawing in background on
the screen while the current image is still shown. This technique
permits to avoid flickering while drawing objects on the screen.
The command
ST7920.REFRESH fmt
permits you to choose between an automatic refresh (ST7920.REFRESH
1)
or a manual refresh (ST7920.REFRESH
0).
By default the refresh is automatic.
When an automatic refresh is set, the image is
immediately updated after each drawing command whereas, with the
manual refresh, the image is refreshed only when an
ST7920.REFRESH command is executed.
The
ST7920.COLOR col
defines the color to be used by the different drawing commands. As
the display is monochrome, only the color 0 (black) and 1(white)
can be defined; an additional color 2 (reverse) permits to draw
objects that reverse the existing color already present on the
screen; useful to draw and clear the same object. By default the
color is 1 (white).
The
ST7920.IMAGE x, y,
image$ permit to draw an image on the screen from a
file. The file format must be XBM, a kind of ‘C’ source code. This
format is not really popular but it is supported by the free tool
Gimp.
The command
ST7920.FONT font_num
permits to define the font to be used by the command
ST7920.PRINT.
There are 4 fonts available, FIXED_5X7, ARIAL
MT10, ARIAL MT16, ARIAL MT24.
Example:
SPI.SETUP
1000000
' set the SPI
at 1MB/sec
ST7920.INIT
15
' init the
ST7920 with the CS at the pin 15
ST7920.CLS
' clear the
screen
ST7920.FONT
2
ST7920.COLOR
1
ST7920.PRINT
10,10,
"HELLO
WORLD"
|
A module based on chipset DS1307 or DS3231 can
be connected to the module using the I2C interface.
These modules are very cheap and available on
Ebay at less than 2€.
This picture shows a DS3231 module which is
very compact and already contains two 4.7K I2C pullups :
The connection is very simple, just 2 pins for
the I2C and the power supply are required.
Available Instructions:
RTC.DATE$[(format)]
RTC.TIME$
RTC.SETTIME Year,
Month, Day, Hours, Minutes, Seconds
The use of the RTC module is very simple.
First the I2C must be initialised with the
command
I2C.SETUP.
Then the date and the time can be read with
the string functions
RTC.TIME$ and
RTC.DATE$.
This time and date can be manually set using
the command
RTC.SETTIME.
The Syntax is :
RTC.SETTIME
Year, Month,
Day, Hours, Minutes, Seconds
Example
Set the date to 02 September 2017 at
13:58:12
RTC.SETTIME 17, 9, 2,
13, 58, 12
Example
I2C.SETUP 4,
5 ' set I2C
port on pins 4 and 5
Print
"The date is
" +
RTC.DATE$
Print
"The time is
" +
RTC.TIME$
|
A PWM / Servo module based on the chip PCA9685
can be connected to the module using the I2C interface.
These modules are very cheap and available on
Ebay at less than 2€.
This picture shows a PCA9685 module that makes
available up to 16 PWM / Servo outputs.
The PCA9685 is an I²C-bus controlled
16-channel controller optimized for Red/Green/Blue/Amber (RGBA)
color backlighting applications. It operates at a programmable
frequency from a typical of 24 Hz to 1526 Hz. All outputs share the
same PWM frequency.
The duty cycle for each output is adjustable
from 0 % to 100 % with 12-bit resolution (4096 steps).
It can also be used to control servo
actuators, simply specifying the PWM frequency at 50 Hz.
This module must be connected using I2C and,
because it already contains two 10K I2C pullups, no external
resistors are required.
Available Instructions:
PCA9685.SETUP address
[,freq]
PCA9685.SETFREQ freq
PCA9685.PWM
pin,
value
In order to use the module, it must be first
set with the command
PCA9685.SETUP address
[,freq]
If not specified, the PWM frequency is set by
default at 1000Hz
Then the PWM frequency can be set with the
command
PCA9685.SETFREQ freq
FInally, the outputs can be driven with the
command
PCA9685.PWM pin,
value
This is an example that drives 2 servos
connected on outputs 0 and 1.
PCA9685.SETUP
&H40,
55
PCA9685.SETFREQ
50
PCA9685.PWM
0,
150
PCA9685.PWM
1,
100
DX
=
1
DY
=
1
MINX
=
100 :
MAXX =
500
MINY
=
150 :
MAXY =
300
X
=
MINX :
Y =
MINY
WHILE
1
PCA9685.PWM
0,
Y
PCA9685.PWM
1,
X
PAUSE
30
PRINT
Y, DY,
MAXY
X =
X
+
DX
Y =
Y
+
DY
IF
(X
<
MINX)
OR
(X
>
MAXX)
THEN
DX
=
-
DX
IF
(Y
<
MINY)
OR
(Y
>
MAXY)
THEN
DY
=
-
DY
WEND
END
|
A display module based on the chipset TM1637
with 4 7-segments display can be connected to the module.
These modules are very cheap and available on
Ebay at around 1€.
This picture shows a module with 4 displays at
0.36”.
The following picture shows another module
with 4 digits at 0.56”.
The following picture shows another module
with 6 digits at 0.56”
Notice that some modules have the “colon”
points in the middle, some have the decimal points and some other
have 6 digits including the decimal points
The connection is very simple, just 2 pins and
the power supply are required.
As the protocol used by this chip is very
similar to the I2C, this display can share the same pins used for
other I2C devices.
Available Instructions are:
TM1637.SETUP data_pin,
clock_pin [,
bit_delay] [,
display_type]
TM1637.PRINT msg$
[, brightness]
To use it, is very simple.
First initialise the display with the command
TM1637.SETUP
With the wiring above, the command must be
:
TM1637.SETUP 4,
5
Note that some modules may already include i2c
pullup resistors on board (so simply try first without).
It is important to highlight
that some display modules may have a capacitor on the input
pins.
In that case it will require an extra
parameter (bit_delay) at the end of the setup command.
This value can be experimentally found, but a
value of 100 should be appropriate for all the modules.
Example :
TM1637.SETUP 4, 5,
100
If a “6 digits display” must be connected,
another extra parameter (display_type)
is required.
Example :
TM1637.SETUP 15 16,
100, 1
The display can be used with the command
TM1637.PRINT msg$
[, brightness]
Where
msg$ is a text string that contains the
message to show
brightness defines the luminosity of
the display from 0 (OFF) to 7 (MAX); if omitted the value is 7
All ASCII
characters can be used but will be shown within the limitation of
the 7 segments of the display.
The decimal point
and the colon (:)
are automatically managed so, to print 12:34 on the display, simply
use
TM1637.PRINT
"12:34" or
TM1637.PRINT
"1.234"
Example
TM1637.SETUP
4,
5
For
i
=
0
to
9999
TM1637.PRINT
str$(i),
4
Next
i
|
A display module based on the chipset TM1638
with 8 7-segments display can be connected to the module.
These modules provide 8 LEDs, 8 Digits
and 8 Keypad Interface.
These modules are very cheap and available on
Ebay at around 2€.
This picture shows a module with 8 digits at
0.36”, 8 leds and 8 buttons
The connection requires 3 pins plus the power
supply.
Available Instructions are:
TM1638.BUTTONS
TM1638.PRINT msg$
[, brightness ]
TM1638.SETUP data_pin,
clock_pin, strobe_pin
TM1638.LEDS val
To use it, is very simple.
First initialise the display with the command
TM1638.SETUP data_pin,
clock_pin, strobe_pin
With the wiring above, the command must be
:
TM1638.SETUP 4, 5,
15
The display can then be used with the command
TM1638.PRINT msg$
[, brightness ]
Where
msg$ is a text string that can contain
up to 8 chars ,
brightness defines the luminosity of
the display from 0 (OFF) to 15 (MAX); if omitted the value is
15
All ASCII
characters can be used but will be shown within the limitation of
the 7 segments of the display.
Example
TM1638.SETUP
4, 5,
15
For
i
=
0
to
9999
TM1638.PRINT
str$(i)
Next
i
|
As the module contains also 8 leds, it is
possible to control them using the command
TM1638.LEDS val.
val is a 8 bit number where each bit is
associated to a led.
Example
TM1638.SETUP
4, 5,
15
For
i
=
0
to
255
TM1638.LEDS
i
Next
i
|
It is also possible to get the status of the
buttons with the function
TM1638.BUTTONS
Example
TM1638.SETUP
4, 5,
15
For
i
=
0
to
5000
Print
TM1638.BUTTONS
Next
i
|
A display module based on the chipset MAX7219
with 8 7-segments display can be connected to the module.
These modules provide 8 Digits
7-segments display including dot points.
These modules are very cheap and available on
Ebay at around 2€.
This picture shows a module with 8 digits at
0.36”.
The wiring is done using the SPI bus plus a
dedicated CS pin.
Available Instructions are:
MAXDISPLAY.SETUP CS_pin
MAXDISPLAY.PRINT msg$
[,brightness]
To use it, is very simple.
First initialise the display with the command
MAXDISPLAY.SETUP CS_pin
With the wiring above, the command must be
:
MAXDISPLAY.SETUP 15
The display can then be used with the command
MAXDISPLAY.PRINT msg$
[,brightness]
Where
msg$ is a text string that can contains
up to 8 chars ,
brightness defines the luminosity of
the display from 0 (OFF) to 15 (MAX); if omitted the value is
15
All ASCII
characters can be used but will be shown within the limitation of
the 7 segments of the display.
Example
MAXDISPLAY.SETUP
15
For
i
=
0
to
9999
MAXDISPLAY.PRINT
str$(i)
Next
i
|
It is also possible to connect dot matrix
modules based on the chipset MAX7219.
These modules contain 4 8x8 dot matrix
displays each one with a dedicated MAX7219 chip.
These modules can be chained in order to
compose a larger display.
The picture shows a module available on Ebay
at around 5€.
The wiring is done using the SPI bus plus a
dedicated CS pin.
Available Instructions are:
MAXSCROLL.SETUP nb_devices,
CS_pin [,reverse]
MAXSCROLL.PRINT msg$
MAXSCROLL.NEXT msg$
MAXSCROLL.TEXT msg$
MAXSCROLL.SHOW position
[, brightness]
MAXSCROLL.SCROLL
[brightness]
MAXSCROLL.OSCILLATE
[brightness]
To use it, the first command required is the
setup of the display.
This can be done with the command
MAXSCROLL.SETUP nb_devices,
CS_pin [,reverse].
The first argument defines
the number of 8x8 displays connected; using the module shown above,
the number is 4.
The 2nd argument defines
the pin used for the CS signal; using the schematic shown above the
pin is 15 (GPIO15).
The 3rd argument, if =1,
permit to use modules with a reversed order digits.
In our case the command
must be :
MAXSCROLL.SETUP
4,
15
The text can then be set on the display with 3
different commands :
1.
MAXSCROLL.PRINT
msg$
2.
MAXSCROLL.NEXT
msg$
3.
MAXSCROLL.TEXT msg$
The first will set the text that will be shown
at the beginning, the 2nd will set the text that will be shown when
the first one will be scrolled out of the display and the 3rd will
permit to modify immediately the text shown.
For example,
MAXSCROLL.PRINT
"Hello"
MAXSCROLL.NEXT
"Friend"
Will permit to show “Hello” at the beginning;
then as soon as “Hello” is scrolled out of the screen, the text
“Friend” will be shown and it will scroll on the display forever
until the next execution of the command
MAXSCROLL.NEXT msg$
The command
MAXSCROLL.TEXT msg$
will permit to modify the text during the scrolling sequence,
useful for “dynamic” messages (i.e time/date information).
The command
MAXSCROLL.SHOW position
[,
brightness] will
permit to move the text in a given position.
The position 1 if the rightmost line of the
display and increasing this value will move the text more on the
left.
Optionally is is possible to define the
brightness of the display.
The last set of commands is composed of
1.
MAXSCROLL.SCROLL
[brightness]
2.
MAXSCROLL.OSCILLATE
[brightness]
The first will permit to scroll the text from
the right to the left and, when the text will be completely
scrolled out, it will restart again with the same text or, if
defined, with the text set with the command
MAXSCROLL.NEXT.
The 2nd will permit to scroll the text from
the right to the left and, when the text will be completely
scrolled out, it will be scrolled back in the opposite direction
until it will reach the initial position, then the process will
restart again.
These 2 commands have an optional parameter
permitting to define the luminosity of the display in a range from
0 (min) to 15 (max); the default value is 0.
As the message requires a continuous
scrolling, these commands must be called on a timed interval (using
a timer).
Let show with an example using the
SCROLL command
'Set 4 8x8
displays with GPIO15 as CS pin
MAXSCROLL.SETUP
4,
15
'Set the
first message as the current time
MAXSCROLL.PRINT
TIME$
'Set the
second message as the current date
MAXSCROLL.NEXT
DATE$
'Set the
refresh rate of the display (50 msec) - lower values -> scroll
faster
TIMER0
50,
SCROLLME
WAIT
SCROLLME:
'Scroll
the display with an intensity of 5
MAXSCROLL.SCROLL
5
RETURN
|
This is another example using the
OSCILLATE command:
'Set 4 8x8
displays with GPIO15 as CS pin
MAXSCROLL.SETUP
4, 15
'Set the
message
MAXSCROLL.PRINT
"Hello
World"
'Set the
refresh rate of the display (50 msec) - lower values -> scroll
faster
TIMER0
50,
SCROLLME
WAIT
SCROLLME:
'Oscillate
the display with an intensity of 5
MAXSCROLL.OSCILLATE
5
RETURN
|
It is possible to connect NeoPixel led strips
based on WS2812B Leds.
These strips are generally available in a
linear form but also as a circular array.
The wiring is very simple as only one output
pin is required for the ESP8266.
This pin must be the pin GPIO2 that is
fixed and cannot be changed.
The strips must be supplied at 5V with a
dedicated power-supply.
As each led could require up-to 60mA, the
power supply must be sized in consequence.
The strip is considered as a sequence of leds
where each one has a position and can have a different color.
From a logical point of view, even the
circular array is seen as a linear strip with a start and end
position.
Then the following commands are available
:
NEO.SETUP nb_led
NEO.STRIP led_start_pos,
led_end_pos, R, G, B [, disable]
NEO.STRIP led_start_pos,
led_end_pos, COLOR [, disable]
NEO.PIXEL led_pos,
R, G, B [, disable]
NEO.PIXEL led_pos,
COLOR [, disable]
NEO.RGB(R,
G, B)
NEO.GETPIXEL(pos)
The first command,
NEO.SETUP [nb_led],
defines the size (in pîxels) of the led strip.
For example
NEO.SETUP 60
defines a strip containing 60 leds (or a ring with 60
leds).
This creates a local memory buffer of the
strip line permitting the manipulation of the colors in the
background.
Then, the leds of the strips can be addressed
taking into account that the first led has the position 0 and the
last has the position (nb_led - 1).
For example, using the declaration
NEO.SETUP 60,
the last led has the position 59.
The leds can then be addressed individually
using the command
NEO.PIXEL or as a block using the commandNEO.STRIP.
For example,
NEO.PIXEL 10, 255,
0, 0 set the led at the position 10 with the color
RED and the command
NEO.STRIP 20, 30, 0,
0, 255 set the leds at the position 20 through 30
with the color BLUE
The colors can be specified as a sequence of 3
numbers from 0 to 255 representing the intensity for the Red, Green
and Blue components or as a single number where the 3 colors are
merged together as a whole.
The function
NEO.RGB(R,
G, B) permits to
generate this merged color.
For example, these 3 commands produce the same
effect :
NEO.STRIP
0, 10, 0,
255, 0
NEO.STRIP
0, 10,
NEO.RGB(0, 255, 0)
NEO.STRIP
0, 10,
65280
The optional argument [,
disable] if set to 1, permit to write in memory without
refreshing the strip. This is useful to manipulate several leds
refreshing the complete line only when required.
For example, with the following program, all
the leds will be refresh one by one at 100ms interval :
NEO.SETUP 60
FOR z
= 0 TO 59
NEO.PIXEL z, 128
PAUSE 100
NEXT z
But, with the following program, all the leds
will be updated in a single shot at the end (after 6 seconds) :
NEO.SETUP 60
FOR z
= 0
TO 59
NEO.PIXEL z, 128,
1
PAUSE 100
NEXT z
NEO.PIXEL 59,
128
It is also possible to connect Dot Matrix
modules based on WS2812B Leds.
These modules contain 64 WS2812B leds
organised in 8x8 matrix.
Several modules can be chained in order to
compose a large display.
The wiring is very simple as only one output
pin is required for the ESP8266.
This pin must be the pin GPIO2 that is
fixed and cannot be changed.
The modules must be supplied at 5V with a
dedicated power-supply.
It must be taken into account that these 8x8
modules can require a lot of current, in particular if all the leds
are at max intensity.
As each led could require up-to 60mA, the
power supply must be sized in consequence.
From a practical point of view, the displays
will never show all the pixels at the same time so we can consider
at least 2 amps per display (so 20 amps for 10 displays).
Available Instructions are:
NEOSCROLL.SETUP nb_devices
[, serpentine]
NEOSCROLL.PRINT msg$
NEOSCROLL.NEXT msg$
NEOSCROLL.COLORS col$
NEOSCROLL.NEXTCOLORS col$
NEOSCROLL.SHOW pos
[, brightness]
NEOSCROLL.TEXT msg$
NEOSCROLL.SCROLL
[brightness]
NEOSCROLL.OSCILLATE
[brightness]
NEOSCROLL.CLS
[refresh]
To use it, the first command required is the
setup of the display.
This can be done with the command
NEOSCROLL.SETUP nb_devices
[, serpentine].
The first argument defines the number of 8x8
modules connected; using the schematic shown above, the number is
4.
The 2nd argument defines if the display itself
is arranged in a linear way or as a serpentine.
If it is 0 (default) the normal layout is
selected, if is 1, the serpentine layout is chosen.
In our case the command must be :
NEOSCROLL.SETUP
4
The text can then be set on the display with 3
different commands :
4.
NEOSCROLL.PRINT
msg$
5.
NEOSCROLL.NEXT
msg$
6.
NEOSCROLL.TEXT msg$
The first will set the text that will be shown
at the beginning, the 2nd will set the text that will be shown when
the first one will be scrolled out of the display and the 3rd will
permit to modify immediately the text shown.
For example,
NEOSCROLL.PRINT
"Hello"
NEOSCROLL.NEXT
"Friend"
Will permit to show “Hello” at the beginning;
then as soon as “Hello” is scrolled out of the screen, the text
“Friend” will be shown and it will scroll on the display forever
until the next execution of the command
NEOSCROLL.NEXT msg$
The command
NEOSCROLL.TEXT msg$
will permit you to modify the text during the scrolling sequence,
useful for “dynamic” messages (i.e time/date information).
As the display permit to show colors, 2 more
commands are available:
1.
NEOSCROLL.COLORS col$
2.
NEOSCROLL.NEXTCOLORS col$
The first defines the colors of the text set
with the commandNEOSCROLL.PRINT
whilst the 2nd defines the colors of the text set with the command
NEOSCROLL.NEXT.
These command
NEOSCROLL.COLORS defines the color of each character of
the text set with the command
NEOSCROLL.PRINT .
The logic is based on a one-to-one
correspondence between the text string and the color string.
The color is based on this table :
COLOR
|
CODE
|
Blue
|
B
|
Green
|
G
|
Cyan
|
C
|
Red
|
R
|
Magenta
|
M
|
Yellow
|
Y
|
Black
|
K
|
White
|
W
|
White
|
Any other
char
|
For example, the commands
NEOSCROLL.PRINT
"Hello"
NEOSCROLL.COLOR
"RGBCM"
Will show the string “Hello” where the first
character is Red, the 2nd Green, the 3rd Blue, ….
With the same logic, the commands
NEOSCROLL.NEXT
"Friend"
NEOSCROLL.NEXTCOLOR
"YWRGBM"
Will show the string “Friend”
where the first character is Yellow, the 2nd White, the 3rd Red,
….
The command
NEOSCROLL.SHOW position
[, brightness] will permit to move the text in
a given position.
The position 1 if the rightmost line of the
display and increasing this value will move the text more on the
left.
Optionally is is possible to define the
brightness of the display.
The last set of commands is composed of
3.
NEOSCROLL.SCROLL
[brightness]
4.
NEOSCROLL.OSCILLATE
[brightness]
The first will permit to scroll the text from
the right to the left and, when the text will be completely
scrolled out, it will restart again with the same text or, if
defined, with the text set with the command
NEOSCROLL.NEXT.
The 2nd will permit to scroll the text from
the right to the left and, when the text will be completely
scrolled out, it will be scrolled back in the opposite direction
until it will reach the initial position, then the process will
restart again.
These 2 commands have an optional parameter
permitting to define the luminosity of the display in a range from
0 (off) to 255 (max); the default value is 5.
Take into account that these displays are very
bright so a luminosity of 5 is already enough.
As the message requires a continuous
scrolling, these commands must be called on a timed interval (using
a timer).
Let show with an example using the
SCROLL command
'Set 4
WS2812B displays with GPIO2 as input
NeoScroll.Setup
4
'Set the
first message as the current time
NeoScroll.print
"TRY Annex
WiFi BASIC"
NeoScroll.colors
"CMYRGCRGBYWRGBCMYRGB"
'Set the
next message
NeoScroll.next
"Hello
World!"
NeoScroll.nextcolors
"BYWRGBCMYRGB"
'Set the
refresh rate of the display (30 msec) - lower values -> scroll
faster
timer0
30,
scrollme
wait
Scrollme:
'Scroll the
display with an intensity of 5
NeoScroll.scroll
5
return
|
This is another example using the
OSCILLATE command:
'Set 4
WS2812B displays with GPIO2 as input
NeoScroll.Setup
4
'Set the
first message as the current time
NeoScroll.print
"000"
NeoScroll.colors
"RBG"
'Set the
refresh rate of the display (50 msec) - lower values -> scroll
faster
timer0
50,
scrollme
'Set the
counter at 0
v
=
0
'Set the
incrementing rate
timer1
100,
increment
wait
Scrollme:
'Scroll the
display with an intensity of 5
NeoScroll.oscillate
5
return
increment:
v =
(v
+
1)
mod
1000
'increment
and limits the value at 999
NeoScroll.text
str$(v,
"%03f")
return
|
A TFT Display based on the chipset ILI9341 can
be connected to the module using the SPI interface.
These displays are available on Ebay at
different sizes from 2.2” to 2.8” and are very cheap.
The resolution of the display is 320 x 240
pixels with 65K colors.
They can also contain a touchscreen controller
that permits receiving feedback from the user via the SPI
interface.
The model shown below is a 2.8” and contains
an interface for the Touch Screen.
As the interface is SPI, the display requires
at least 5 pins when connected as a display only and 6 when the
touch screen is enabled.
The image below shows an 2.8” display provided
with touch screen interface:
These displays have a little jumper
zone (J1) that must be solder-bridged if powering the module from
3.3V else it will be configured to work at 5v.
Wiring for display only
Wiring for Display and
Touchscreen
In order to use the
TFT, the first step is to initialise the Display
This
can be done with the following commands :
TFT.INIT 16, 4, 1 ‘ (16 is the pin for CS, 4 is the pin for
D/C, 1 is the landscape orientation)
The
display is initialized, by default, for a SPI speed of 40
Mhz.
In case
of problems (spurious pixels and lines) this can be changed using
the command SPI.SETUP speed.
Example
SPI.SETUP 20000000 ‘ set the speed at 20 Mhz.
The
display can then be cleared with the command
TFT.FILL 0
The
display is now ready to receive drawing commands.
The
list of commands available is :
TFT.CIRCLE, TFT.LINE, TFT.PRINT, TFT.RECT, TFT.TEXT.COL,
TFT.TEXT.POS, TFT.TEXT.SIZE, TFT.BMP
The
list of the functions available is:
TFT.RGB
The
color is defined as a number between 0 and 65535; this corresponds
to the color format named 565 where 5 bits are dedicated to Red, 6
to green and 5 to blue.
The
function TFT.RGB permits to specify the R,G,B components as numbers
from 0 to 255.
For
example the function RGB(255,0,0) defines the color RED and
TFT.RGB(0,255,0) defines the color green.
Look at
the documentation for the details of each command.
Example
:
tft.init
16, 4, 1
tft.fill
0
for
r =
0
to
30000
step
0.02
d=r/6
s=sin(r)*sin(5*r+d)*140+160
c=cos(r)*sin(5*r+d)*100+120
tft.circle
s,c,10,rnd(65535),1
next
r
|
This is a little
pong game given as a TFT example.
It uses an iphone
as a remote controller using an UDP connection.
The iphone
application is the RCW controller already shown in the I/O buffer
chapter
' simple pong example using the remote UDP
controller
' RCW controller for IOS (ipad and iphone)
' control the paddle with up and down arrow
' the controller must be connected using the port
10000
udp.begin
10000
onudp
received
' set the TFT
tft.init
16, 4, 3
tft.fill
0
tft.text.size
2
' text size of the score
tft.text.col
tft.rgb(0,
255, 0),
0
' color of the score
speed
=
5
' speed of game. lower is faster
dx
=
1
dy
=
1
x
=
100
y
=
100
py
=
120
py_p
=
py
dp
=
0
score
=
0
tft.rect
0, py-30,
4, 30, 63488, 1
'paddle
while
1
tft.rect
x, y, 10, 10, 0, 1
' clear the ball
x
=
x
+
dx
y
=
y
+
dy
tft.rect
x, y, 10, 10, 65535, 1
'redraw the ball in the new position
'limits the position of the ball
if
(x
<=
0)
or
(x
>=
319)
then
dx
=
-
dx
if
(y
<=
0)
or
(y
>=
239)
then
dy
=
-
dy
py_p =
py
py
=
py
+
dp
if
py_p
<>
py
then
if
py
>
py_p
then
tft.rect
0, py, 4, 1 , 63488, 1
'paddle
tft.rect
0, py-31
, 4, 1 , 0, 1
'paddle
else
tft.rect
0, py+1
, 4, 1 , 0, 1
'paddle
tft.rect
0, py-30,
4, 1 , 63488, 1
'paddle
end
if
end
if
' limits of the paddle
if
(py
<=
30)
or
(py
>=
239)
then
dp
=
0
pause
speed
' determine the speed of the game
tft.text.pos
280, 0
tft.print
score
if
(x
=
4)
then
pp =
py
-
y
if
(pp
>=0
) and
(pp
<
35
)
then
score =
score
+
1
if
(pp
<
10)
then
dy
=
1
if
(pp
>
20)
then
dy
=
-1
dx =
1
else
tft.rect
0, py-30,
4, 30, 63488, 1
'redraw paddle
tft.rect
x, y, 10, 10, 0, 1
'clear ball
pause
1000
score =
0
'reset the score
dx =
1
if
rnd(2)
=
0
then
dy =
1
else
dy =
-1
y =
rnd(239)
end
if
end
if
wend
received:
udp.read_iobuff(0)
' use the byte 1 top detemine the button pushed
arrows =
iobuff.read(0,
1)
if
arrows =
1
then
dp =
-1
if
arrows =
2
then
dp =
1
if
arrows =
0
then
dp =
0
return
|
The touchscreen
functionality permits to get the position of the point pressed on
the screen.
It is associated
with the event OnTouch and the functions TOUCH.X and
TOUCH.Y.
The command
Touch.Setup defines the pin used for the Touchscreen.
Example:
Touch.Setup
15
OnTouch
touchme
wait
touchme:
print
"touched",
touch.x,
touch.y
return
|
A TFT Display based on the chipset ST7735 can
be connected to the module using the SPI interface.
These displays are available on Ebay at
different sizes and are very cheap.
The resolution is variable in function of the
display and is in general available with a resolution going
from 80 x 160 to 128 x
160 pixels with 65K colors.
The pictures below shows a 1.8” model with a
resolution of 128 x 160 with a RED
TAB
The pictures below shows a 1.44” model with a
resolution of 128 x 128 with a GREEN
TAB
The pictures below shows a 0.96” model with a
resolution of 80 x 160
These displays may have a little jumper
zone (J1) that must be solder-bridged if powering the module from
3.3V else it will be configured to work at 5v.
This display can be
controlled using the same commands as for the ILI9341 except for
the initialisation command TFT.INIT that has specific
parameters:
TFT.INIT CS_PIN,
DC_PIN, Orientation, Display_width, Display_height,
Variant
CS_PIN
defines the pin where CS signal is connected
DC_PIN
defines the pin where DC signal is connected
Orientation
defines the orientation of the display (see table below)
Display_height
defines the height (in pixels) of the display in portrait
orientation
Display_width
defines the height (in pixels) of the display in portrait
orientation
Variant
defines the variant of the display (see text below)
Variant
define the type of display, originally this was based on the colour
of the tab on the screen protector film but this is not always
true, so try out the different options below if the screen does
not display graphics correctly, e.g. colours wrong, mirror images,
or tray pixels at the edges.
Variant
|
Display model
|
0
|
Green Tab (default)
|
1
|
Red Tab
|
2
|
Black Tab
|
3
|
Green Tab 2
|
4
|
Green Tab 3
|
5
|
Green Tab 128 x 128
|
6
|
Green Tab 80 x 160
|
7
|
Red Tab 80 x 160
|
8
|
Other
|
Orientation
|
Display model
|
0
|
Portrait
|
1
|
Landscape
|
2
|
Portrait reversed
|
3
|
Landscape reversed
|
The display is
initialized, by default, for a SPI speed of 40 Mhz and this is in
general too fast for these displays.
In case of problems
(spurious pixels and lines) this can be changed using the command
SPI.SETUP speed.
Example
SPI.SETUP 20000000
‘ set the speed at 20 Mhz.
Initialisation examples :
Initialise the first display (1.8” model with
a resolution of 128 x 160 with a RED
TAB )
TFT.INIT 16, 4, 1,
128, 160, 1
Initialise the 2nd display (1.44” model with a
resolution of 128 x 128 with a GREEN
TAB )
TFT.INIT 16, 4, 1,
128, 128, 5
Initialise the 3rd display ( 0.96” model with
a resolution of 80 x 160 )
TFT.INIT 16, 4, 1,
80, 160, 6
Look at the
documentation for the details of each command.
Example :
' setup a
display 128 x 128
tft.init
16, 4, 1, 128, 128, 5
' reduce the
speed at 20 Mhz
Spi.setup
20000000
tft.fill
0
for
r =
0
to
30000
step
0.02
d=r/6
s=sin(r)*sin(5*r+d)*50+64
c=cos(r)*sin(5*r+d)*50+64
tft.circle
s,c,10,rnd(65535),1
next
r
|
An infrared receiver can be connected to the
module permitting it to decode messages received from RC remote
controllers.
It is also possible to connect an IR led
permitting to generate RC codes from the module.
This picture shows a kit containing an IR
receiver, an IR LED and a controller available on ebay at around
1€.
The following drawing shows an example of
connection using the pins GPIO12 and GPIO14:
Details of wiring for the VS1838B
There are several commands associated with the
IR functions :
IR.INIT,
IR.GET$,
IR.SEND and
ONINFRARED.
In order to use the Infrared functions, the
first command to use is IR.INIT.
This command defines the pins to be used for
the IR receiver and the IR transmitter.
As per the wiring given above, the command
must be:
IR.INIT 14,
12
IMPORTANT : the pin 16 cannot be
used!
The command
ONINFRARED defines the label where the program will jump
when a code is received by the Infrared receiver. Then, using the
function
IR.GET$ it will be possible to retrieve the code of the
message received:
Example
IR.INIT
14,
12
ONINFRARED
irReceived
Wait
irReceived:
PRINT
IR.GET$
RETURN
|
The transmission can be done using the command
IR.SEND:
The format is
IR.SEND format,
code$, bits
Example for a NEC code:
IR.SEND 3,
"20DF40BF", 32
The following formats are supported for the
reception and the transmission :
VALUE
|
FORMAT
|
-1
|
UNKNOWN
|
0
|
UNUSED
|
1
|
RC5
|
2
|
RC6
|
3
|
NEC
|
4
|
SONY
|
5
|
PANASONIC
|
6
|
JVC
|
7
|
SAMSUNG
|
This is an example working with the RC
controller shown in the picture above.
It shows the status of the button 1 to 8
pressed on the web page and can control 8 leds wired to a PCF8574
using the I2C bus :
oninfrared
irReceived
onHtmlReload
mypage
l1
=
0:
l2 =
0:
l3=0:
l4=0:
l5=0:
l6=0:
l7=0:
l8=0
ir.init
14
i2c.setup
4,5
l
=
0
PCF8574_write
l
gosub
mypage
wait
irReceived:
print
ir.get$,
ir.get$(1),
ir.get$(2),
val("&h"
+
ir.get$(3)),
ir.get$(4),
ir.get$(5)
code_type
=
val(ir.get$(1))
address
=
val(ir.get$(2))
cmd
=
val("&h"
+
ir.get$(3))
' if NEC
CODE
if
code_type
=
3
then
' if RC
address is 0
if
address
=
0
then
if
cmd
=
22
then
l
=
l
xor
1
if
cmd
=
25
then
l
=
l
xor
2
if
cmd
=
13
then
l
=
l
xor
4
if
cmd
=
12
then
l
=
l
xor
8
if
cmd
=
24
then
l
=
l
xor
16
if
cmd
=
94
then
l
=
l
xor
32
if
cmd
=
8
then
l
=
l
xor
64
if
cmd
=
28
then
l
=
l
xor
128
PCF8574_write l
setleds l
end
if
end
if
return
mypage:
cls
a$
=
""
a$
=
a$
+
|<h1>
TEST OF IR REMOTE CONTROLLER COUPLED<br>|
a$
=
a$
+
| WITH AN I2C
PCF8574 AND 8 LEDS</h1>|
a$
=
a$
+
led$(l1)
+
led$(l2)
+
led$(l3)
+
led$(l4)
+
led$(l5)
+
led$(l6)
+
led$(l7)
+
led$(l8)
html
a$
return
sub
setleds(x)
' set the
status for the leds
l1 =
(x
and
1)
l2 =
(x
and
2)
l3 =
(x
and
4)
l4 =
(x
and
8)
l5 =
(x
and
16)
l6 =
(x
and
32)
l7 =
(x
and
64)
l8 =
(x
and
128)
refresh
end
sub
sub
PCF8574_write(x)
i2c.begin
32
'PCF8574
i2c.write
x
i2c.end
end
sub
|
An Ultrasonic distance sensor HC-SR04 can be
connected to the module.
This sensor permits to measure the distance
from a target positioned in front in a range going from a minimum
of 3 cm to a maximum of 3 meters.
For the connection, it requires 2 pins plus
the power supply. (5 Volts).
There is just one function,
DISTANCE(pin_trig,
pin_echo) that returns the distance from the target in
cm.
Example:
' Measure
the distance from the target 2 times / second
print
"DISTANCE
MEASUREMENT"
for
i
=
0
to
1000
print
str$(DISTANCE(15,12),
"%4f")
+
"cm"
pause
500
next
i
end
|
A Temperature / Humidity sensor of the DHTxx
family can be connected.
The picture below the ones that are actually
supported.
These sensors requires a single wire
connection like shown below:
To use them, is very simple.
First initialise the sensor with the command
DHT.SETUP pin,
model
The pin can be any available pin of the device
and model can be 11, 21 or 22 (for DHT11, DHT21 or DHT22).
Assuming that we are using the DHT22 on the
pin GPIO2, the command must be :
DHT.SETUP 2,
22
Then 3 functions are available :
DHT.TEMP
DHT.HUM
DHT.HEATINDEX
The first returns the value of the temperature
in °C
The 2nd returns the value of the Humidity in
%
The 3rd returns the value of the heat index in
°C
.
Example
DHT.SETUP
2
,22
Print
"The
Temperature is ";
DHT.TEMP
;
"°C"
Print
"The Humidity
is ";
DHT.HUM
;
"%"
Print
"The Heat
Index is ";
DHT.TEMP
;
"°C"
|
One or several DS18B20 Temperature
sensors can be connected.
The picture below shows the ones that are
actually supported.
These Dallas 1-wire sensors use a single wire
connection as shown below, allowing multiple sensors to be
connected in parallel on the same 1-wire bus from a single gpio
pin.
There is just one function available :
TEMPR$(pin_number,
[ID])
This function will return the temperature or
the ID of the device depending on the parameter ‘ID’ specified.
In the schematic above, to read the 3
temperatures, the example code is :
Print
"The
Temperature 1 is ";TEMPR$(2,
1)
;
"°C"
Print
"The
Temperature 2 is ";TEMPR$(2,
2)
;
"°C"
Print
"The
Temperature 3 is ";TEMPR$(2,
3)
;
"°C"
|
To read the temperature from a unique device
connected, the code is :
Print
"The
Temperature is ";TEMPR$(2,
1)
;
"°C"
|
It is important to note that, if the ID is
not specified, the command will return the Hex address of the
device(s) connected.
A BNO055 Absolute Orientation Sensor can be
connected to the module using the I2C interface.
This sensor contains 3 accelerometers, 3 gyros
and 3 magnetometers BUT also contains an integrated 32 bits
controller running Bosch Sensortec sensor fusion software.
This frees the ESP8266 the module from
all the calculations related to the implementation of a Fusion
algorithm.
This component is quite expensive ( ~10 €)
compared to the classic MPU6050, MPU9250, ... but the quality of
the internal fusion algo enables to use it without any effort.
Before connecting it, the links S0 and S1 must
be soldered with the ‘-’ position, as shown in the picture, to
enable I2C.
The connection is very simple, just 2 pins for
the I2C bus and the power supply are required.
The module is already provided with on-board
pull-up resistors, so external pull-up resistors are not
required.
Available instructions are :
BNO055.SETUP(address)
BNO055.HEADING
BNO055.PITCH
BNO055.ROLL
BNO055.VECTOR ( param )
BNO055.CALIB [(param)]
The use of the BNO055 module is very
simple.
First the I2C must be initialised with the
command
I2C.SETUP.
Then the module must be initialised with the
function
BNO055.SETUP(address).
‘address’ must be &h28 if the pin ‘I2C’ is
connected to GND or &h29 if connected to VCC.
This function returns 1 if the module has been
initialised properly otherwise it returns 0.
After the initialisation, the euler angles can
be simply read using the corresponding functions
BNO055.HEADING,
BNO055.PITCH and
BNO055.ROLL
Another useful function is
BNO055.CALIB
[(param)] that
returns the calibration status of the BNO055 internal sensors.
If used without any parameters, it returns 1
when all the internal sensors are calibrated otherwise it returns
0.
The BNO055 is put in auto calibration mode so
it will calibrate by itself in background.
Refers to the following link for more
information :
BNO055 Calibration
Example
i2c.setup
4, 5
' set I2C port on pins 4 and 5
if
bno055.setup(&h28)
=
0
then
print
"BNO module not found"
end
end
if
for
z =
1
to
1000
print
"Pitch:",
bno055.pitch
print
"Roll:",
bno055.roll
print
"Heading:",
bno055.heading
print
"Calibrated:",
bno055.calib
pause 100
next
z
end
|
A BME280 Sensor can be connected to the module
using the I2C interface.
This unit combines individual high linearity,
high accuracy sensors for pressure, humidity and temperature.
A unit based on the chip BMP280 can also be
used, except that this one doesn’t contain the humidity sensor.
The connection is very simple, just 2 pins for
the I2C bus and the power supply are required.
The module is already provided with on-board
pull-up resistors, so external pull-up resistors are not
required.
Available instructions are :
BME280.SETUP(address)
BME280.ALT(qnh)
BME280.HUM
BME280.QFE
BME280.QNH(altitude)
BME280.TEMP
The use of the BME280 module is very
simple.
First the I2C must be initialised with the
command
I2C.SETUP.
Then the module must be initialised with the
function
BME280.SETUP(address).
‘address’ must be &h76 if the pin ‘SDO’ is
connected to GND or &h77 if connected to VCC.
This function returns 1 if the module has been
initialised properly otherwise it returns 0.
After the initialisation, the temperature,
pressure and humidity can be simply read using the corresponding
functions
BME280.TEMP,
BME280.QFE and
BME280.HUM.
The function
BME280.ALT(qnh)
returns the altitude information but requires, as a parameter, the
pressure at sea level.
At the opposite, the function
BME280.QNH(altitude)
returns the sea level pressure but requires, as a parameter, the
current altitude.
Example
I2C.SETUP
4,
5 ' set I2C
port on pins 4 and 5
if
bme280.setup(&h76)
=
0
then
print
"BME280 not
found" :
end
for
z
=
1
to
1000
print
"Temperature",
bme280.temp
print
"Humidity",
bme280.hum
print
"Pressure",
bme280.qfe
qnh =
bme280.qnh(150)
' assume the
altitude at 150 meters
print
"Qnh
", qnh
print
"Altitude",
bme280.alt(1019)
' assume a
sea level pressure at 1019 Hpa
pause
100
next
z
|
An APDS9960 Sensor can be connected to the
module using the I2C interface.
The APDS-9960 device features advanced Gesture
detection, Proximity detection, Digital Ambient Light Sense (ALS)
and Color Sense (RGBC).
Gesture detection utilizes four directional
photodiodes to sense reflected IR energy (sourced by the integrated
LED) to convert physical motion information (i.e. direction and
distance) to digital information.
The connection is very simple, just 2 pins for
the I2C bus and the power supply are required.
The module is already provided with on-board
pull-up resistors, so external pull-up resistors are not
required.
Available functions are :
APDS9960.SETUP(mode)
APDS9960.READGESTURE
APDS9960.AMBIENT
APDS9960.RED
APDS9960.GREEN
APDS9960.BLUE
APDS9960.PROXIMITY
APDS9960.GESTUREGAIN
APDS9960.GESTURELED
There is also an
associated
ONGESTURE event.
The use of the
APDS9960 module is quite simple.
First the I2C must be
initialised with the command
I2C.SETUP.
Then the module must be initialised with the
function
APDS9960.SETUP(mode).
“mode”
must be 1 for “GESTURE” mode.
As soon as a gesture is recognised, the event
ONGESTURE is triggered, so it then possible to get the
recognised gesture with the function
APDS9960.READGESTURE
Example
'APDS9960
GESTURE SENSOR DEMO
i2c.setup
4,5
'set the sensor in GESTURE mode
if
apds9960.Setup(1)
=
0
then
print
"APDS9960 not found"
:
end
'define the Gesture event
ongesture
gesture
'Wait for the event
wait
gesture:
r
=
apds9960.ReadGesture
select
case
r
case
1
print
"LEFT"
case
2
print
"RIGHT"
case
3
print
"UP"
case
4
print
"DOWN"
case
5
print
"NEAR"
case
6
print
"FAR"
case
else
print
"none"
end
select
return
|
The module can also be configured as Ambient
& RGB light sensor.
In this case “mode”
must be 2 for “Ambient Light and RGB Color” mode.
Example
'APDS9960
LIGHT SENSOR DEMO
i2c.setup
4,5
'set the sensor in Ambient light and RGB Color
mode
if
apds9960.Setup(2)
=
0
then
print
"APDS9960 not found"
:
end
for z
=
1 to 10000
print
"Light:",
apds9960.ambient,
apds9960.red,
apds9960.green,
apds9960.blue
pause
100
next z
end
|
The module can, finally, be configured also as
a Proximity sensor.
In this case “mode”
must be 3 for “Proximity” mode.
Example
'APDS9960
PROXIMITY SENSOR DEMO
i2c.setup
4,5
'set the sensor in Proximity mode
if
apds9960.Setup(3)
=
0
then
print
"APDS996 |