// wrench_I2CScanner.ino
//
// 10 nov 2022
//
// Test sketch for a I2C bus interface to the Arduino Wire library with wrench.
//
// This Wokwi project: https://wokwi.com/projects/347893115064943188
//
// 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/
//
#include <Wire.h>
#include <hd44780.h> // The hd44780 library
#include <hd44780ioClass/hd44780_I2Cexp.h> // i2c expander i/o class header
#include "wrench.h"
// 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 ----------------
println( "Hello from wrench code");
println( "I2C Scanner");
Wire::begin();
println( "Scanning...");
nDevices = 0;
for( address = 1; address < 127; address++)
{
Wire::beginTransmission(address);
error = Wire::endTransmission();
if( error == 0)
{
print("I2C device found at address ");
// print the address as hexadecimal
buffer[16];
str::sprintf( buffer, "0x%2X", address);
print( buffer);
println( " !");
nDevices++;
}
}
if (nDevices == 0)
println("No I2C devices found\n");
else
println("done\n");
while(true)
{
delay(10);
}
// -------------------------------------------------
// End of wrench source code
// ------------------------------------------------- */
)=====";
hd44780_I2Cexp lcd; // declare lcd object: auto locate & auto config expander chip
const int LCD_COLS = 16;
const int LCD_ROWS = 2;
// 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 digitalRead( 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 Wire_begin( WRValue* stackTop, const int argn, WRContext* c);
void Wire_beginTransmission( WRValue* stackTop, const int argn, WRContext* c);
void Wire_write( WRValue* stackTop, const int argn, WRContext* c);
void Wire_endTransmission( WRValue* stackTop, const int argn, WRContext* c);
void Wire_requestFrom( WRValue* stackTop, const int argn, WRContext* c);
void Wire_available( WRValue* stackTop, const int argn, WRContext* c);
void Wire_read( WRValue* stackTop, const int argn, WRContext* c);
void setup()
{
Serial.begin( 115200 );
Serial.println( "wrench_I2CScanner.ino");
Serial.print( "wrench version: ");
Serial.print( WRENCH_VERSION_MAJOR);
Serial.print( ".");
Serial.println( WRENCH_VERSION_MINOR);
Serial.println( "┃ To compile the wrench source code, ┃");
Serial.println( "┃ remove #define WRENCH_WITHOUT_COMPILER ┃");
Serial.println( "┃ from wrench.h ┃");
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_loadStringLib( w); // for sprintf
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, "digitalRead", 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);
wr_registerLibraryFunction( w, "Wire::begin", Wire_begin);
wr_registerLibraryFunction( w, "Wire::beginTransmission", Wire_beginTransmission);
wr_registerLibraryFunction( w, "Wire::write", Wire_write);
wr_registerLibraryFunction( w, "Wire::endTransmission", Wire_endTransmission);
wr_registerLibraryFunction( w, "Wire::requestFrom", Wire_requestFrom);
wr_registerLibraryFunction( w, "Wire::available", Wire_available);
wr_registerLibraryFunction( w, "Wire::read", Wire_read);
unsigned char* outBytes; // compiled code is alloc'ed
int outLen;
int err = wr_compile( wrenchCode, strlen(wrenchCode), &outBytes, &outLen); // compile it
Serial.print( "The Compiled code is ");
Serial.print( outLen);
Serial.println( " bytes");
if( err == 0)
{
wr_run( w, outBytes); // 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
}
}
}
// Assuming that LOW is 0 and HIGH is 1
void digitalWrite( WRState* w, const WRValue* argv, const int argn, WRValue& retVal, void* usr)
{
if( argn == 2)
{
digitalWrite(argv[0].asInt(), argv[1].asInt());
}
}
// Assuming that LOW is 0 and HIGH is 1
void digitalRead( WRState* w, const WRValue* argv, const int argn, WRValue& retVal, void* usr)
{
if( argn == 1)
{
int value = digitalRead(argv[0].asInt());
wr_makeInt( &retVal, value);
}
}
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);
}
}
void Wire_begin( WRValue* stackTop, const int argn, WRContext* c)
{
if( argn == 0)
{
Wire.begin();
}
}
void Wire_beginTransmission( WRValue* stackTop, const int argn, WRContext* c)
{
if( argn == 1)
{
int address = stackTop[-1].asInt(); // first argument
Wire.beginTransmission( address);
}
}
void Wire_write( WRValue* stackTop, const int argn, WRContext* c)
{
if( argn == 1)
{
int data = stackTop[-1].asInt();
Wire.write( data);
}
else if( argn == 2)
{
// To be added !
}
}
void Wire_endTransmission( WRValue* stackTop, const int argn, WRContext* c)
{
if( argn == 0)
{
int error = Wire.endTransmission();
wr_makeInt( stackTop, error);
}
else if( argn == 1)
{
int stop = stackTop[-1].asInt();
int error = Wire.endTransmission( stop);
wr_makeInt( stackTop, error);
}
}
void Wire_requestFrom( WRValue* stackTop, const int argn, WRContext* c)
{
if( argn == 2)
{
int address = stackTop[-2].asInt(); // first argument
int bytes = stackTop[-1].asInt(); // second argument
int n = Wire.requestFrom( address, bytes);
wr_makeInt( stackTop, n);
}
}
void Wire_available( WRValue* stackTop, const int argn, WRContext* c)
{
if( argn == 0)
{
int n = Wire.available();
wr_makeInt( stackTop, n);
}
}
void Wire_read( WRValue* stackTop, const int argn, WRContext* c)
{
if( argn == 0)
{
int data = Wire.read();
wr_makeInt( stackTop, data);
}
}
/*
void I2C_Scanner()
{
Wire.begin();
int nDevices = 0;
for( int address = 1; address < 127; address++)
{
Wire.beginTransmission(address);
error = Wire.endTransmission();
if (error == 0)
{
Serial.print("I2C device found at address 0x");
if( address < 0x10)
Serial.print("0");
Serial.print( address, HEX); // to be done: print as hex
Serial.println(" !");
nDevices++;
}
}
if (nDevices == 0)
Serial.println("No I2C devices found\n");
else
Serial.println("done\n");
}
*/