B9X Basic Language Reference
A complete guide to B9X Basic v1.03, including language syntax, variables, control flow, user functions, GPIO, storage, MQTT, audio, OpenAI services and real-time VoIP through B9X Voice Server.
Version 1.03Required function-return syntax
Every function returns a value. A numeric/double-returning function must be used in a numeric assignment. A string-returning function must be used in a string assignment beginning with LET. Calling either type as a stand-alone statement causes a compile error.
' Correct numeric/double return
retval = somefunction();
' Correct string return
let retstr$ = somefunction$();
' Incorrect — return value has nowhere to go
somefunction();
somefunction$();
Examples throughout this reference follow this rule, including initialization, output and stop functions that are often treated like procedures in other languages.
1. Language fundamentals
B9X Basic is a compact embedded language. Programs contain executable statements and optional SUB and FUNCTION definitions. Simple statements end with a semicolon. Block endings such as ENDIF, WEND, NEXT, END SUB and END FUNCTION do not.
' Minimal program
let greeting$ = "Hello from B9X";
counter = 3;
print greeting$;
print counter;
| Item | Rule |
|---|---|
| Numeric identifier | Begins with a letter or underscore; remaining characters may include digits. |
| String identifier | Uses the same rules and ends with $. |
| Identifier length | 31 stored characters maximum. |
| Numbers | Decimal integer, decimal fraction or scientific notation, such as 12, 3.5 or 1.2e-3. |
| Strings | Text enclosed in double quotation marks. |
| Case | Names are case-sensitive. Use function spelling exactly as shown. |
| Truth | Zero is false; any nonzero numeric value is true. |
Comments
' Apostrophe comment
// C++-style comment
/* Block comment
across lines */
2. Values, variables and arrays
All ordinary numeric variables and numeric function results are double-precision values. String names end in $, hold up to 512 characters and require LET when assigned.
temperature = 72.5;
let title$ = "B9X Basic";
dim samples[9]; ' indices 0 through 9
samples[0] = 12.5;
retval = samples[0];
The declared array bound is inclusive. String arrays are not implemented. Variables created outside a function are global. Function parameters temporarily hold local argument values and are restored when the function returns. A function can still read or modify other global variables.
3. Expressions and operators
| Order | Operators | Meaning |
|---|---|---|
| 1 | ( ) | Grouping and function calls |
| 2 | + - NOT ! | Unary sign and logical negation |
| 3 | ** | Exponentiation, right-associative |
| 4 | * / | Multiplication and division |
| 5 | + - | Addition/subtraction; + also concatenates strings |
| 6 | < <= > >= | Relational comparison |
| 7 | == != | Equality and inequality |
| 8 | AND && | Logical AND |
| 9 | OR || | Logical OR |
result = 2 + 3 * 4;
power = 2 ** 3 ** 2;
enabled = temperature >= 25 and alarm == 1;
let message$ = "Value: " + str$(result);
Use == for comparison. A single = performs assignment.
4. Statements and control flow
if temperature > 80 then
print "High";
else
print "Normal";
endif
count = 0;
while count < 5 do
print count;
count = count + 1;
wend
for i = 0 to 10 step 2 do
print i;
next
STEP defaults to 1 and may be negative. Do not use zero. Braces may form an explicit statement block. B9X Basic does not implement line numbers, GOTO, GOSUB, INPUT, DATA/READ, SELECT CASE, BREAK or CONTINUE.
5. Subroutines and user functions
SUB
A SUB is an argument-free procedure invoked with CALL. It may use RETURN; to exit early.
sub blink()
retval = setHigh(2);
retval = wait(100);
retval = setLow(2);
end sub
call blink();
Numeric FUNCTION
function clamp(value, low, high)
if value < low then
return low;
endif
if value > high then
return high;
endif
return value;
end function
retval = clamp(120, 0, 100);
String FUNCTION
function greeting$(name$)
return "Hello, " + name$;
end function
let retstr$ = greeting$("B9X");
A user function accepts up to eight numeric or string parameters. String parameter and function names end in $. Define a function before the code that calls it.
6. Core string functions
len double
len(text$)Returns the character count.
retval = len("B9X");left$ string
left$(text$, count)Returns up to count characters from the left. Negative counts act as zero; oversized counts are capped.
let retstr$ = left$("B9X Basic", 3);right$ string
right$(text$, count)Returns up to count characters from the right.
let retstr$ = right$("B9X Basic", 5);mid$ string
mid$(text$, start, count)Extracts characters beginning at the 1-based start position.
let retstr$ = mid$("B9X Basic", 5, 5);chr$ string
chr$(code)Creates a one-character string from a byte value.
let retstr$ = chr$(65);asc double
asc(text$)Reads the byte value of the first character.
retval = asc("A");str$ string
str$(number)Converts a number to compact text.
let retstr$ = str$(12.5);val double
val(text$)Parses a leading decimal floating-point number.
retval = val("12.5 volts");instr double
instr(text$, find$) or instr(start, text$, find$)Searches for text, optionally beginning at a 1-based position.
retval = instr("B9X Basic", "Basic");
retval = instr(6, "banana", "a");7. Formatting and time
fmt$ string
fmt$(number, format$)Formats a number. Tokens include 0 required digit, # optional digit, decimal point, comma grouping, percent, sign, scientific notation and positive;negative;zero sections.
let retstr$ = fmt$(1234.5, "#,##0.00");| Function | Example | Return |
|---|---|---|
hour() | retval = hour(); | 0–23 |
minute() | retval = minute(); | 0–59 |
second() | retval = second(); | 0–59 |
month() | retval = month(); | 1–12 |
day() | retval = day(); | Day of month |
year() | retval = year(); | Four-digit year |
8. Mathematics
All functions in this table return a double and therefore must be assigned.
| Function | Description | Correct example |
|---|---|---|
abs(x) | Absolute value. | retval = abs(-3.5); |
min(a,b) | Smaller argument. | retval = min(4,9); |
max(a,b) | Larger argument. | retval = max(4,9); |
round(x) | Nearest integral value. | retval = round(2.6); |
sqrt(x) | Square root. | retval = sqrt(81); |
exp(x) | e raised to x. | retval = exp(1); |
log(x) | Natural logarithm. | retval = log(2.71828); |
log10(x) | Base-10 logarithm. | retval = log10(1000); |
sin(radians) | Sine. | retval = sin(0); |
cos(radians) | Cosine. | retval = cos(0); |
tan(radians) | Tangent. | retval = tan(0); |
asin(x) | Inverse sine in radians. | retval = asin(1); |
acos(x) | Inverse cosine in radians. | retval = acos(1); |
atan(x) | Inverse tangent in radians. | retval = atan(1); |
sinh(x) | Hyperbolic sine. | retval = sinh(0); |
cosh(x) | Hyperbolic cosine. | retval = cosh(0); |
rnd(min,max) | Random integer in the inclusive range. | retval = rnd(1,6); |
modulo(a,b) | Integer remainder after truncation. Divisor cannot be zero. | retval = modulo(17,5); |
9. System, Wi-Fi and nonvolatile storage
wifiIsConnected double
wifiIsConnected()Checks the current Wi-Fi connection state.
retval = wifiIsConnected();memx double
memx()Reads free 8-bit-capable heap memory.
retval = memx();setNVS double
setNVS(name$, value$)Saves a string under a named key in nonvolatile flash storage.
retval = setNVS("station", "workbench");getNVS$ string
getNVS$(name$)Retrieves a previously stored NVS string.
let retstr$ = getNVS$("station");10. GPIO, timing, ADC and temperature
| Function | Description | Correct example | Return |
|---|---|---|---|
wait(ms) | Delays the B9X program while background tasks continue. | retval = wait(250); | Supplied delay |
getTicks(mode) | Mode 0 seconds, 1 milliseconds, 2 microseconds. | retval = getTicks(1); | Elapsed time |
makeInput(gpio) | Input with internal pulldown. | retval = makeInput(4); | GPIO argument |
getInput(gpio) | Reads a digital input. | retval = getInput(4); | 0 or 1 |
makeOutput(gpio) | Configures a digital output. | retval = makeOutput(2); | GPIO argument |
setHigh(gpio) | Sets an output high. | retval = setHigh(2); | GPIO argument |
setLow(gpio) | Sets an output low. | retval = setLow(2); | GPIO argument |
initADC() | Initializes the ADC. | retval = initADC(); | 1 |
readADCRaw(channel) | Reads a raw ADC conversion. | retval = readADCRaw(3); | Raw count |
readADCMV(channel) | Reads calibrated millivolts. | retval = readADCMV(3); | Millivolts |
deinitADC() | Releases the ADC. | retval = deinitADC(); | 1 |
readTemperature(gpio) | Reads a DS18B20 sensor. | retval = readTemperature(18); | Degrees Celsius |
11. Display, motion, WAV and local TTS
| Function | Description | Correct example |
|---|---|---|
displayInit(sda,scl) | Initializes a 128×64 SSD1306 OLED at address 0x3C. | retval = displayInit(8,9); |
displayText(text$) | Displays text; ~ ends each display line. | retval = displayText("B9X~ready~"); |
motionInit(port,tx_gpio) | Initializes the supported motion-distance sensor. | retval = motionInit(1,17); |
motionGetDistance() | Reads the latest distance. | retval = motionGetDistance(); |
playInit(bclk,ws,data) | Initializes WAV output. | retval = playInit(5,4,6); |
play(filename$) | Starts a WAV file from SPIFFS. Supply the filename relative to /spiffs/. | retval = play("alert.wav"); |
isPlaying() | Checks WAV playback. | retval = isPlaying(); |
ttsInit(port,busy,tx,rx) | Initializes a local SYN6988 speech module. | retval = ttsInit(1,7,17,18); |
ttsIsBusy() | Checks local speech activity. | retval = ttsIsBusy(); |
ttsSpeak(text$) | Submits text to the local speech module. | retval = ttsSpeak("System ready."); |
WAV playback supports uncompressed 44.1 kHz, 16-bit stereo PCM WAV files.
12. OpenAI text, transcription and TTS
| Function | Description | Correct example |
|---|---|---|
oaixInit(api_key$) | Initializes OpenAI text questions. | retval = oaixInit(b9x_key1$); |
oaixAskQuestion(id,question$) | Queues a question with an application-defined request ID. | retval = oaixAskQuestion(1,"Name one moon of Mars."); |
oaixGetAnswer$() | Polls the next answer. | let retstr$ = oaixGetAnswer$(); |
vrxInit(bclk,ws,data,seconds,key$) | Initializes INMP441 recording and transcription. | retval = vrxInit(4,5,6,5,b9x_key1$); |
vrxTranscribe(id) | Queues one record-and-transcribe operation. | retval = vrxTranscribe(100); |
vrxGetTranscript$() | Polls the next completed transcript. | let retstr$ = vrxGetTranscript$(); |
ttsxInit(bclk,ws,data,key$,voice$,style$) | Initializes OpenAI speech and PCM5102 output. | retval = ttsxInit(15,16,17,b9x_key1$,"marin","Speak clearly."); |
ttsxSpeak(text$) | Queues up to 255 characters for synthesis and playback. | retval = ttsxSpeak("Hello from B9X."); |
Answer and transcript polling functions return an empty string while no result is ready. A 401 response normally means the API key is invalid.
13. B9X Voice Server VoIP
The VoIP functions connect an ESP32-S3 with an INMP441 microphone and PCM5102 audio output to B9X Voice Server. The server provides low-latency group voice communication between embedded nodes, Windows clients and Android clients.
B9X Basic v1.03 uses 11,025 Hz audio, UDP port 24000 locally, encrypted audio and periodic keep-alive messages.
voipInit double
voipInit(tx_bclk, tx_ws, tx_data, rx_bclk, rx_ws, rx_data)Initializes PCM5102 output using the first three pins and INMP441 input using the final three pins. The INMP441 L/R pin is expected low for the left slot.
retval = voipInit(15, 16, 17, 4, 5, 6);voipConnect double
voipConnect(server$, port, password$, node_name$, encryption_key$)Copies the connection settings, resolves the server and begins an asynchronous connection. The encryption key must match the B9X Voice Server configuration and should contain exactly 32 characters.
let server$ = "192.168.1.100";
serverPort = 18000;
let password$ = "serverPassword";
let nodeName$ = "Workshop ESP32";
let encryptionKey$ = b9x_key2$;
retval = voipConnect(server$, serverPort, password$, nodeName$, encryptionKey$);voipIsConnected() to confirm completion.voipIsConnected double
voipIsConnected()Checks whether the B9X Voice Server connection is active.
retval = voipIsConnected();voipStartListening double
voipStartListening()Starts nonblocking reception and PCM5102 playback of audio sent by other clients.
retval = voipStartListening();voipHaltListening double
voipHaltListening()Stops received-audio playback and releases the transmit I²S channel.
retval = voipHaltListening();voipStartSending double
voipStartSending()Starts nonblocking INMP441 capture and sends microphone audio to connected clients through the server.
retval = voipStartSending();voipHaltSending double
voipHaltSending()Stops microphone transmission and releases the receive I²S channel.
retval = voipHaltSending();voipIsSending double
voipIsSending()Checks whether microphone transmission is active.
retval = voipIsSending();voipClose double
voipClose()Sends the close command, stops both audio directions and closes the server session.
retval = voipClose();Complete push-to-talk pattern
retval = voipInit(15, 16, 17, 4, 5, 6);
retval = voipConnect("192.168.1.100", 18000, "password", "B9X Node", b9x_key2$);
' Wait for asynchronous connection
connected = 0;
while connected == 0 do
connected = voipIsConnected();
retval = wait(100);
wend
' Listen initially
retval = voipStartListening();
' Begin push-to-talk
retval = voipHaltListening();
retval = voipStartSending();
' End push-to-talk
retval = voipHaltSending();
retval = voipStartListening();
14. Infrared remote control
irInit double
irInit(gpio)Starts nonblocking reception using a demodulated 38 kHz IR receiver. Common formats include Sony/SIRC.
retval = irInit(15);irGetCode double
irGetCode()Polls immediately for an event with confidence above 90.
retval = irGetCode();15. WS2812 addressable LEDs
| Function | Description | Correct example |
|---|---|---|
ws2812Init(gpio,count) | Creates a strip and returns its handle. | strip = ws2812Init(48,8); |
ws2812Fill(handle,r,g,b) | Sets all pixels in the local buffer. | retval = ws2812Fill(strip,0,0,32); |
ws2812SetPixel(handle,index,r,g,b) | Sets one zero-based pixel. | retval = ws2812SetPixel(strip,0,32,0,0); |
ws2812Show(handle) | Transmits buffered colors. | retval = ws2812Show(strip); |
ws2812Clear(handle) | Turns all pixels off. | retval = ws2812Clear(strip); |
ws2812IsBusy(handle) | Checks strip transmission. | retval = ws2812IsBusy(strip); |
Color-changing functions return 0 on success or an error code. ws2812IsBusy returns nonzero while transmission is active.
16. MQTT
| Function | Description | Correct example |
|---|---|---|
initMQTTClient(uri$,user$,password$) | Starts an MQTT client. | retval = initMQTTClient("mqtt://broker.local","user","password"); |
mqttClientSubscribe(topic$) | Subscribes at QoS 0. | retval = mqttClientSubscribe("b9x/input"); |
mqttClientUnsubscribe(topic$) | Removes a subscription. | retval = mqttClientUnsubscribe("b9x/input"); |
mqttClientPublish(topic$,payload$) | Publishes at QoS 1, retain disabled. | retval = mqttClientPublish("b9x/status","ready"); |
mqttClientGetMessage$() | Polls one queued MQTT event. | let retstr$ = mqttClientGetMessage$(); |
Numeric operations return a message identifier or error value. The polling function returns an empty string when nothing is ready, otherwise [TYPE][id][topic][data]. Event types include DATA, CONNECT, DISCONNECT, SUBSCRIBED, UNSUBSCRIBED, PUBLISHED and ERROR.
17. File logging and reset information
logData double
logData(filename$, text$)Appends one text line to a file. Supply a filename relative to the configured filesystem root.
retval = logData("data.csv", "72.5,Running");getResetType double
getResetType()Reads the reset-type value captured when the runtime started.
retval = getResetType();restart double
restart()Immediately restarts the ESP32.
retval = restart();18. Supplied user-function templates
These functions demonstrate adding native C functions with numeric, string and mixed argument types.
| Function | Correct example | Return |
|---|---|---|
user1(x) | retval = user1(10); | x * 2 |
user2(a,b) | retval = user2(10,20); | a + b |
usermix(text$,number) | retval = usermix("ABC",10); | Number plus text length |
usermix$(text$,number) | let retstr$ = usermix$("ABC",10); | Text, space and number |
19. Protected key variables
B9X Basic supplies five protected string variables for passwords, API keys and encryption keys:
b9x_key1$
b9x_key2$
b9x_key3$
b9x_key4$
b9x_key5$
Each holds up to 512 characters and can be passed to OpenAI, MQTT, NVS or VoIP functions. Values may be updated through the setup menu without displaying the current value. For B9X Voice Server, store the 32-character AES key in one protected variable instead of embedding it in a shared BASIC program.
20. Complete programming patterns
Safe polling pattern
retval = oaixInit(b9x_key1$);
retval = oaixAskQuestion(1, "Give one short electronics fact.");
let answer$ = "";
answerLength = len(answer$);
while answerLength == 0 do
let answer$ = oaixGetAnswer$();
retval = wait(50);
answerLength = len(answer$);
wend
print answer$;
GPIO debounce
retval = makeInput(4);
last = getInput(4);
while 1 do
current = getInput(4);
if current != last then
retval = wait(20);
current = getInput(4);
if current != last then
print current;
last = current;
endif
endif
retval = wait(5);
wend
Common mistakes
| Symptom | Likely cause | Correction |
|---|---|---|
| Compile error at a function call | Return value was discarded. | Use retval = function(); or let retstr$ = function$();. |
| String assignment error | LET omitted. | Write let name$ = expression;. |
| Comparison behaves incorrectly | Used = instead of ==. | Use == for equality. |
| Repeated requests or speech | Queue function called every loop iteration. | Submit once and poll separately. |
| Empty asynchronous result | Work has not completed. | Poll with a short delay. |
| I²S controller busy | Two features own the same audio direction. | Stop the current VoIP/TTS/VR operation before starting the conflicting feature. |
| VoIP never connects | Wrong server address, port, password/key, Wi-Fi state or blocked UDP. | Confirm Wi-Fi, server port 18000 unless changed, matching 32-character key and firewall forwarding. |
Appendix: limits and quick reference
| Language item | Maximum |
|---|---|
| String value | 512 characters |
| Identifier | 31 characters |
| Numeric arrays | 64 |
| Elements per numeric array | 1024; indices 0–1023 |
| User FUNCTION definitions | 64 |
| User FUNCTION parameters | 8 |
| OpenAI TTS message | 255 characters per submission |
' Numeric assignment
name = expression;
let name = expression;
' String assignment
let name$ = string_expression;
' Function results
retval = numericFunction(arguments);
let retstr$ = stringFunction$(arguments);
' Array
dim values[9];
values[index] = expression;
' Control flow
if expression then ... else ... endif
while expression do ... wend
for name = start to limit step amount do ... next
' Procedures and functions
sub name() ... end sub
call name();
function name(parameters) ... return expression; ... end function
function name$(parameters) ... return string_expression; ... end function
The information provided here by B9X Electronics (‘we,’ ‘us,’ or ‘our’), in this document,
is for general informational purposes only. All information in this document
is provided in good faith, however, we make no representation or warranty of any kind,
express or implied, regarding the accuracy, adequacy, validity, reliability, availability,
or completeness of any information in this document. Under no circumstance shall we have any liability
to you for any loss or damage of any kind incurred as a result of the use of this document
or reliance on any information provided in this document.

