B9X Basic

B9X Electronics Documentation

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.03

Required 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;
ItemRule
Numeric identifierBegins with a letter or underscore; remaining characters may include digits.
String identifierUses the same rules and ends with $.
Identifier length31 stored characters maximum.
NumbersDecimal integer, decimal fraction or scientific notation, such as 12, 3.5 or 1.2e-3.
StringsText enclosed in double quotation marks.
CaseNames are case-sensitive. Use function spelling exactly as shown.
TruthZero 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

OrderOperatorsMeaning
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
8AND &&Logical AND
9OR ||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");
Returns: Numeric character count.

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);
Returns: A string.

right$ string

right$(text$, count)

Returns up to count characters from the right.

let retstr$ = right$("B9X Basic", 5);
Returns: A string.

mid$ string

mid$(text$, start, count)

Extracts characters beginning at the 1-based start position.

let retstr$ = mid$("B9X Basic", 5, 5);
Returns: A string clipped at the source boundary.

chr$ string

chr$(code)

Creates a one-character string from a byte value.

let retstr$ = chr$(65);
Returns: One character.

asc double

asc(text$)

Reads the byte value of the first character.

retval = asc("A");
Returns: Byte code, or 0 for an empty string.

str$ string

str$(number)

Converts a number to compact text.

let retstr$ = str$(12.5);
Returns: Numeric text.

val double

val(text$)

Parses a leading decimal floating-point number.

retval = val("12.5 volts");
Returns: Parsed number, or 0 if parsing cannot begin.

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");
Returns: 1-based match position, or 0 when not found.

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");
Returns: Formatted string.
FunctionExampleReturn
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.

FunctionDescriptionCorrect 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();
Returns: Nonzero when connected; 0 when disconnected.

memx double

memx()

Reads free 8-bit-capable heap memory.

retval = memx();
Returns: Available bytes.

setNVS double

setNVS(name$, value$)

Saves a string under a named key in nonvolatile flash storage.

retval = setNVS("station", "workbench");
Returns: Storage operation result.

getNVS$ string

getNVS$(name$)

Retrieves a previously stored NVS string.

let retstr$ = getNVS$("station");
Returns: Stored text, or an empty string when unavailable.

10. GPIO, timing, ADC and temperature

FunctionDescriptionCorrect exampleReturn
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

FunctionDescriptionCorrect 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

Nonblocking operation: submit each request once, then poll its result with a short delay. Do not submit the request on every loop pass.
FunctionDescriptionCorrect 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);
Returns: 0 on success or an ESP-IDF error code.

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$);
Returns: 0 when the connection command is sent successfully; otherwise an ESP-IDF error code. Use voipIsConnected() to confirm completion.

voipIsConnected double

voipIsConnected()

Checks whether the B9X Voice Server connection is active.

retval = voipIsConnected();
Returns: 1 when connected; 0 otherwise.

voipStartListening double

voipStartListening()

Starts nonblocking reception and PCM5102 playback of audio sent by other clients.

retval = voipStartListening();
Returns: 0 on success or an ESP-IDF error code.

voipHaltListening double

voipHaltListening()

Stops received-audio playback and releases the transmit I²S channel.

retval = voipHaltListening();
Returns: 0 on success or an ESP-IDF error code.

voipStartSending double

voipStartSending()

Starts nonblocking INMP441 capture and sends microphone audio to connected clients through the server.

retval = voipStartSending();
Returns: 0 on success or an ESP-IDF error code.

voipHaltSending double

voipHaltSending()

Stops microphone transmission and releases the receive I²S channel.

retval = voipHaltSending();
Returns: 0 on success or an ESP-IDF error code.

voipIsSending double

voipIsSending()

Checks whether microphone transmission is active.

retval = voipIsSending();
Returns: 1 while sending; 0 otherwise.

voipClose double

voipClose()

Sends the close command, stops both audio directions and closes the server session.

retval = voipClose();
Returns: 0 on success or an ESP-IDF error code.

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();
Audio resource rule: VoIP listening and OpenAI TTS both use the PCM5102/I²S output. Stop VoIP listening before TTS playback. VoIP sending and voice recognition both use the INMP441/I²S input. Stop VoIP sending before starting transcription. Listening and sending may be initialized together, but conflicting operations must not actively own the same I²S direction simultaneously.

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);
Returns: 1 after startup.

irGetCode double

irGetCode()

Polls immediately for an event with confidence above 90.

retval = irGetCode();
Returns: Decoded command, or -1 when none is ready.

15. WS2812 addressable LEDs

FunctionDescriptionCorrect 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

FunctionDescriptionCorrect 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");
Returns: 0 on success or a filesystem error code.

getResetType double

getResetType()

Reads the reset-type value captured when the runtime started.

retval = getResetType();
Returns: Numeric reset reason/type.

restart double

restart()

Immediately restarts the ESP32.

retval = restart();
Returns: The function normally does not return because the device restarts.

18. Supplied user-function templates

These functions demonstrate adding native C functions with numeric, string and mixed argument types.

FunctionCorrect exampleReturn
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

SymptomLikely causeCorrection
Compile error at a function callReturn value was discarded.Use retval = function(); or let retstr$ = function$();.
String assignment errorLET omitted.Write let name$ = expression;.
Comparison behaves incorrectlyUsed = instead of ==.Use == for equality.
Repeated requests or speechQueue function called every loop iteration.Submit once and poll separately.
Empty asynchronous resultWork has not completed.Poll with a short delay.
I²S controller busyTwo features own the same audio direction.Stop the current VoIP/TTS/VR operation before starting the conflicting feature.
VoIP never connectsWrong 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 itemMaximum
String value512 characters
Identifier31 characters
Numeric arrays64
Elements per numeric array1024; indices 0–1023
User FUNCTION definitions64
User FUNCTION parameters8
OpenAI TTS message255 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

Return to top ↑

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.