// FloatingWrench.ino
//
// 9 nov 2022, Version 1, by Koepel
// Initial version. Using wrench 1.80
// 16 nov 2022, Version 2, by Koepel
// Updated to wrench 2.00
// 17 nov 2022, Version 3, by Koepel
// Updated to wrench 2.01
// Wrench version 2.01 is now printed as "2.01" instead of "2.1".
// Calculated the time to compile the wrench code.
//
// Test sketch for the floating point calculations of the 'wrench' interpreter.
// When the simulation is running, click on the NTC module and change the settings.
//
// This Wokwi project: https://wokwi.com/projects/347808271933899348
//
// wrench repository: https://github.com/jingoro2112/wrench
// wrench website: http://northarc.com/wrench/www/
// I learned about 'wrench' here: https://forum.arduino.cc/t/c-like-interpreter-that-actually-fits-and-runs-inside-most-arduino-chips-wrench/1050025/
//
// To compile the wrench code on the Raspberry Pi Pico itself, be sure that
// the "#define WRENCH_WITHOUT_COMPILER" is not defined in "wrench.h".
//
// Old times: The processor executes code.
// Modern times:
// The wrench code is compiled into binary code.
// That binary code is executed by the wrench interpreter.
// The wrench interpreter runs on top of Arduino code.
// The Arduino code runs on top of Mbed.
// The Mbed runs on low level Raspberry Pi Pico code.
// The low level Raspberry Pi Pico code is compiled.
// The compiled code runs in JavaScipt.
// The JavaScript code runs in your browser.
// Your browser uses your operating system and your processor.
//
#include <Wire.h>
#include <hd44780.h> // The hd44780 library
#include <hd44780ioClass/hd44780_I2Cexp.h> // i2c expander i/o class header
#include "wrench.h"
hd44780_I2Cexp lcd; // declare lcd object: auto locate & auto config expander chip
const int LCD_COLS = 16;
const int LCD_ROWS = 2;
// The wrench code that will be compiled runtime.
// Using the "raw string literal" or "Super Quotes"
// https://en.cppreference.com/w/cpp/language/string_literal
//
const char* wrenchCode = R"=====(
// -------------------------------------------------
// Start of wrench source code
// -------------------------------------------------
// -------------------- Glue code ------------------
enum { INPUT = 0, OUTPUT = 1}; // make own definitions
enum { LOW = 0, HIGH = 1};
// ----------------- Start of Arduino-alike code ----------------
print( "Hello 😀 "); // also testing UTF-8
println( "This is wrench code 🔧");
// ----------------- Calculating Pi ----------------
println( "Calculating π");
two = 2.0;
s = 0.0;
t = 1.0;
for( i = 0; i < 11; i++)
{
r = s + two;
s = math::sqrt(r);
t *= s / two;
pi = two / t;
print( pi);
print( " ");
if( (i + 1) % 5 == 0) // five values per line
println();
}
println();
// --------- Show temperature on LCD display ------------
ntcPin = 26;
ledPin = 22;
ledStatus = LOW;
pinMode( ledPin, OUTPUT);
status = lcd::begin( 16, 2); // columns, rows
if( status != 0)
{
println( "Error, LCD was not found");
}
while(true)
{
pos = 0; // keep track of how many characters are writen
lcd::setCursor(0,0);
pos += lcd::print( "T = ");
// Formula taken from: https://wokwi.com/projects/299330254810382858 (C)Uri Shaked
BETA = 3950.0; // should match the Beta Coefficient of the thermistor
analogValue = analogRead( ntcPin);
celsius = 1.0 / (math::log(1.0 / (1023.0 / analogValue - 1.0)) / BETA + 1.0 / 298.15) - 273.15;
pos += lcd::print( celsius);
// clear the rest of the line
for( i=pos; i<16; i++)
lcd::print( " ");
// Blink the led
if( ledStatus == LOW)
ledStatus = HIGH;
else
ledStatus = LOW;
digitalWrite( ledPin, ledStatus);
delay( 300);
}
// -------------------------------------------------
// End of wrench source code
// ------------------------------------------------- */
)=====";
// The Arduino preprocessor gets confused, therefor function prototyping is required
void print( WRState* w, const WRValue* argv, const int argn, WRValue& retVal, void* usr);
void println( WRState* w, const WRValue* argv, const int argn, WRValue& retVal, void* usr);
void delay( WRState* w, const WRValue* argv, const int argn, WRValue& retVal, void* usr);
void pinMode( WRState* w, const WRValue* argv, const int argn, WRValue& retVal, void* usr);
void digitalWrite( WRState* w, const WRValue* argv, const int argn, WRValue& retVal, void* usr);
void analogRead( WRState* w, const WRValue* argv, const int argn, WRValue& retVal, void* usr);
void lcd_begin( WRValue* stackTop, const int argn, WRContext* c);
void lcd_setCursor( WRValue* stackTop, const int argn, WRContext* c);
void lcd_print( WRValue* stackTop, const int argn, WRContext* c);
void setup()
{
Serial.begin( 115200 );
Serial.println( "FloatingWrench.ino");
Serial.print( "wrench version: ");
Serial.print( WRENCH_VERSION_MAJOR);
Serial.print( ".");
if( WRENCH_VERSION_MINOR < 10)
Serial.print( "0");
Serial.println( WRENCH_VERSION_MINOR);
WRState* w = wr_newState(); // create the state
// Bind the functions.
// The Math functions are provide by wrench.
// The normal functions can even have the same names.
// Adding a library function uses basic wrench functionality.
wr_loadMathLib( w); // register all the math functions
wr_registerFunction( w, "print", print);
wr_registerFunction( w, "println", println);
wr_registerFunction( w, "delay", delay);
wr_registerFunction( w, "pinMode", pinMode);
wr_registerFunction( w, "digitalWrite", digitalWrite);
wr_registerFunction( w, "analogRead", analogRead);
wr_registerLibraryFunction( w, "lcd::begin", lcd_begin);
wr_registerLibraryFunction( w, "lcd::setCursor", lcd_setCursor);
wr_registerLibraryFunction( w, "lcd::print", lcd_print);
unsigned char* outBytes; // compiled code is alloc'ed
int outLen;
unsigned long T1 = millis();
int err = wr_compile( wrenchCode, strlen(wrenchCode), &outBytes, &outLen); // compile it
unsigned long T2 = millis();
unsigned long elapsedMillis = T2 - T1;
Serial.print( "Compiling took ");
Serial.print( elapsedMillis);
Serial.print( " ms, the compiled code is ");
Serial.print( outLen);
Serial.println( " bytes.");
if( err == 0)
{
wr_run( w, outBytes, outLen); // load and run the code!
delete[] outBytes; // clean up
}
else
{
Serial.print( "Compiling error: ");
Serial.println( err);
Serial.print( "Did you forget to remove the #define WRENCH_WITHOUT_COMPILER");
}
wr_destroyState( w );
}
void loop()
{
delay( 10); // a delay in the loop is better for Wokwi
}
// The functions convert the wrench code to the specific platform code.
void print( WRState* w, const WRValue* argv, const int argn, WRValue& retVal, void* usr)
{
char buf[128];
for( int i=0; i<argn; ++i)
{
Serial.print( argv[i].asString(buf, sizeof(buf)));
}
}
void println( WRState* w, const WRValue* argv, const int argn, WRValue& retVal, void* usr)
{
char buf[128];
for( int i=0; i<argn; ++i)
{
Serial.print( argv[i].asString(buf, sizeof(buf)));
}
Serial.println(); // allow no arguments for just a newline
}
void delay( WRState* w, const WRValue* argv, const int argn, WRValue& retVal, void* usr)
{
if( argn == 1)
{
delay( (unsigned long)argv[0].asInt()); // a signed int will work up to 25 days
}
}
// No translation is needed for the LOW and HIGH in the wrench code,
// they seem to be 0 and 1 no every platform.
// The INPUT and OUTPUT can have differenct numbers on different platforms,
// those need an extra translation.
void pinMode( WRState* w, const WRValue* argv, const int argn, WRValue& retVal, void* usr)
{
if( argn == 2)
{
if ( argv[1].asInt() == 1) // wrench value for OUTPUT
{
pinMode( argv[0].asInt(), OUTPUT); // Arduino value for OUTPUT
}
else if ( argv[1].asInt() == 0) // wrench value for INPUT
{
pinMode( argv[0].asInt(), INPUT); // Arduino value for INPUT
}
}
}
void digitalWrite( WRState* w, const WRValue* argv, const int argn, WRValue& retVal, void* usr)
{
if( argn == 2)
{
digitalWrite(argv[0].asInt(), argv[1].asInt());
}
}
void analogRead( WRState* w, const WRValue* argv, const int argn, WRValue& retVal, void* usr)
{
if ( argn == 1)
{
int value = analogRead(argv[0].asInt());
wr_makeInt( &retVal, value); // put the value in the return package
}
}
void lcd_begin( WRValue* stackTop, const int argn, WRContext* c)
{
if( argn == 2)
{
int columns = stackTop[-2].asInt(); // first argument
int rows = stackTop[-1].asInt(); // second argument
int status = lcd.begin( columns, rows);
wr_makeInt( stackTop, status);
}
}
void lcd_setCursor( WRValue* stackTop, const int argn, WRContext* c)
{
if( argn == 2)
{
int column = stackTop[-2].asInt(); // first argument
int row = stackTop[-1].asInt(); // second argument
lcd.setCursor( column, row);
}
}
void lcd_print( WRValue* stackTop, const int argn, WRContext* c)
{
if( argn == 1)
{
char buf[60];
int n = lcd.print( stackTop[-1].asString(buf, sizeof(buf)));
wr_makeInt( stackTop, n);
}
}
double FrancoisVieteFormula()
{
double two = 2.0;
double s = 0.0;
double t = 1.0;
for( int i = 0; i < 10; i++)
{
double r = s + two;
s = sqrt(r);
t *= s / two;
}
double pi = two / t;
return( pi);
}