Solduino is a comprehensive embedded software development kit (SDK) for interacting with the Solana blockchain from Arduino and ESP32 microcontrollers. It provides tools for wallet generation, transaction signing, and RPC communication.
- ✅ RPC Communication: Full-featured Solana RPC client for ESP32
- ✅ Wallet Generation: Generate and manage Solana keypairs with Ed25519
- ✅ Transaction Signing: Build, sign, and serialize Solana transactions
- ✅ Transaction Serialization: Serialize transactions to Solana wire format
- ✅ Message Signing: Sign messages with Ed25519 keypairs
- ✅ HTTPS Support: Secure connections using WiFiClientSecure
- ✅ Arduino Compatible: Works with Arduino IDE and PlatformIO
- Arduino IDE 1.8.13+ or PlatformIO
- ESP32 Board Support Package installed
- WiFi connection for RPC communication
- Open Arduino IDE
- Go to Sketch → Include Library → Manage Libraries
- Search for "Solduino" or "sol"
- Click Install
- Download or clone this repository:
git clone https://github.com/torrey-xyz/solduino.git
- Copy the
solfolder to your Arduinolibrariesdirectory:- Windows:
Documents\Arduino\libraries\ - macOS:
~/Documents/Arduino/libraries/ - Linux:
~/Arduino/libraries/
- Windows:
- Restart Arduino IDE
If you haven't installed ESP32 support:
- In Arduino IDE, go to File → Preferences
- Add this URL to Additional Board Manager URLs:
https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json - Go to Tools → Board → Boards Manager
- Search for "ESP32" and install esp32 by Espressif Systems
This library depends on:
- ArduinoJson (v6.19.0+) - Available via Library Manager
- WiFi - Built into ESP32
- HTTPClient - Built into ESP32
- WiFiClientSecure - Built into ESP32
Install ArduinoJson via Library Manager if not already installed.
Optional libraries for sensor examples:
- DHT sensor library - For
examples/temperature_dht22_demo/ - Adafruit Unified Sensor - Common dependency for DHT library
- max6675 - For
examples/temperature_thermocouple_demo/ - TinyGPSPlus - For
examples/gps_neo7m_demo/
#include <WiFi.h>
#include <rpc_client.h>
const char* ssid = "YOUR_WIFI_SSID";
const char* password = "YOUR_WIFI_PASSWORD";
const String DEVNET_RPC = "https://api.devnet.solana.com";
RpcClient solanaClient(DEVNET_RPC);
void setup() {
Serial.begin(115200);
// Connect to WiFi
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nWiFi connected!");
// Initialize RPC client
if (solanaClient.begin()) {
Serial.println("RPC Client initialized!");
// Get network version
String version = solanaClient.getVersion();
Serial.println("Solana Version: " + version);
// Get current slot
String slot = solanaClient.getSlot();
Serial.println("Current Slot: " + slot);
}
}
void loop() {
// Your code here
}This section describes the architecture and organization of the Solduino library.
Solduino is organized into three main components as required for embedded software development:
- Core Embedded Software Development Kit (SDK)
- Modules (Wallet Generation, Transaction Signing, RPC Communication)
- Setup Instructions (provided in Installation section)
sol/
├── README.md # Main documentation and setup instructions
├── library.properties # Arduino library metadata
├── LICENSE # License file (to be added)
│
├── solduino.h # Core SDK entry point
├── solduino.cpp # Core SDK implementation
│
├── rpc_client.h # RPC Communication Module (header)
├── rpc_client.cpp # RPC Communication Module (implementation)
│
├── keypair.h # Wallet Generation Module (header)
├── keypair.cpp # Wallet Generation Module (implementation)
├── crypto.h # Cryptographic Utilities (public header: Base58, address helpers)
├── crypto_internal.h # Cryptographic Utilities (internal Ed25519 primitives)
├── crypto.cpp # Cryptographic Utilities (implementation)
│
├── transaction.h # Transaction Module (header)
├── transaction.cpp # Transaction Module (implementation)
├── serializer.h # Transaction Serialization Module (header)
├── serializer.cpp # Transaction Serialization Module (implementation)
│
└── examples/
├── basic_rpc_demo/
│ └── basic_rpc_demo.ino # Basic RPC operations example
├── sensor_to_chain_demo/
│ └── sensor_to_chain_demo.ino # Generic sensor -> chain workflow
├── temperature_thermistor_demo/
│ └── temperature_thermistor_demo.ino # Thermistor temperature push
├── temperature_thermocouple_demo/
│ └── temperature_thermocouple_demo.ino # MAX6675 thermocouple push
├── temperature_dht22_demo/
│ └── temperature_dht22_demo.ino # DHT22 temp+humidity push
├── air_mq135_demo/
│ └── air_mq135_demo.ino # MQ-135 air quality push
├── gps_neo7m_demo/
│ └── gps_neo7m_demo.ino # NEO-7M GPS position push
├── wallet_demo/
│ └── wallet_demo.ino # Wallet generation and management example
└── transaction_demo/
├── transaction_demo.ino # Transaction creation and signing example
├── transfer_demo.ino # SOL transfer example
└── airdrop_demo.ino # Airdrop request example
Purpose: Provides foundational functionality, version management, and acts as the main entry point for the library.
Files:
solduino.h- Main SDK header with:- Version information and constants
- Library-wide configuration
- Module includes
- Core SDK class definition
solduino.cpp- SDK implementation with:- Version accessors
- Library initialization
- SDK information display
Key Features:
- Version management (major.minor.patch)
- Library-wide constants (URL lengths, key sizes, etc.)
- RPC endpoint constants (mainnet, devnet, testnet)
- Commitment level enums
- Centralized module access
Usage:
#include <solduino.h>
Solduino sdk;
sdk.begin();
Serial.println(sdk.getVersion());Purpose: Enables communication with Solana RPC endpoints.
Features:
- HTTPS support via WiFiClientSecure
- Account information retrieval
- Balance queries
- Transaction submission and status checking
- Block and slot information
- Token account operations
- Network health monitoring
- Custom RPC calls
Key Classes:
RpcClient- Main RPC client classAccountInfo- Account information structureBlockInfo- Block information structureTransactionResponse- Transaction response structure
Usage:
#include <solduino.h> // Includes rpc_client.h
RpcClient client(SOLDUINO_DEVNET_RPC);
client.begin();
String balance = client.getBalance(publicKey);Purpose: Generate and manage Solana keypairs on embedded devices.
Features:
- Ed25519 keypair generation using hardware random number generator
- Import wallets from private keys (bytes or Base58 format)
- Import wallets from seeds
- Public/private key management
- Base58 encoding/decoding for Solana addresses
- Message signing with Ed25519
- Signature verification
Files:
keypair.h/keypair.cpp- Keypair class for wallet managementcrypto.h/crypto.cpp- Cryptographic utilities (Base58, Ed25519, SHA-512)
Usage:
#include <solduino.h>
// Generate new keypair
Keypair keypair;
keypair.generate();
// Get public address
char address[64];
keypair.getPublicKeyAddress(address, sizeof(address));
Serial.println(address);
// Get private key (Base58)
char privateKey[128];
keypair.getPrivateKeyBase58(privateKey, sizeof(privateKey));
// Import from private key (Base58)
Keypair imported;
imported.importFromPrivateKeyBase58("YourPrivateKeyBase58");
// Import from seed
uint8_t seed[32];
// ... set seed ...
imported.importFromSeed(seed);
// Sign a message
String message = "Hello, Solana!";
uint8_t signature[64];
keypair.sign(message, signature);
// Verify a signature
bool isValid = keypair.verify((uint8_t*)message.c_str(), message.length(), signature);Purpose: Build and sign Solana transactions.
Features:
- Transaction construction with multiple instructions
- Built-in transfer instruction helper
- Custom instruction support
- Transaction signing with single or multiple keypairs
- Message building with accounts and blockhash
- Account management (signers, writable, readonly)
Files:
transaction.h/transaction.cpp- Transaction and Message classes
Key Classes:
Transaction- Main transaction classMessage- Transaction message builderInstruction- Instruction representation
Usage:
#include <solduino.h>
// Create a transfer transaction
Transaction tx;
uint8_t fromPubkey[32], toPubkey[32];
// Get blockhash from RPC
String blockhashStr = client.getLatestBlockhash();
// Parse blockhash to bytes...
// Add transfer instruction
tx.addTransferInstruction(fromPubkey, toPubkey, 1000000); // 1 SOL
// Set recent blockhash
tx.setRecentBlockhash(blockhash);
// Sign transaction
tx.sign(payerKeypair);
// Multi-signer: pass an array of Keypair pointers (clears then applies in order)
const Keypair* signers[] = { &feePayer, &cosigner };
tx.sign(signers, 2);
// Offline / multi-party flows: build up signatures with partialSign()
tx.partialSign(feePayer);
// ...ship tx bytes to the cosigner, who calls tx.partialSign(cosigner)...
// Low-level (legacy) raw-bytes form -- still supported:
// tx.sign(privateKey, fromPubkey);
// tx.signMultiple(privateKeys, publicKeys, 2);Purpose: Serialize Solana transactions to wire format for RPC submission.
Features:
- Serialize transactions to Solana compact array format
- Base64 encoding for RPC submission
- Base58 encoding support
- Message serialization
- Size calculation for buffer allocation
Files:
serializer.h/serializer.cpp- TransactionSerializer and Base64 classes
Key Classes:
TransactionSerializer- Transaction serialization utilitiesBase64- Base64 encoding/decoding utilities
Usage:
#include <solduino.h>
// After creating and signing a transaction
Transaction tx;
// ... build and sign transaction ...
// Serialize to base64 (for RPC submission)
char serializedTx[2048];
if (TransactionSerializer::encodeTransaction(tx, serializedTx, sizeof(serializedTx))) {
// Send to RPC
String result = client.sendTransaction(String(serializedTx));
}
// Or serialize to raw bytes first
uint8_t buffer[2048];
uint16_t serializedLen;
if (TransactionSerializer::serializeTransaction(tx, buffer, sizeof(buffer), serializedLen)) {
// Encode to base64
char base64[4096];
Base64::encode(buffer, serializedLen, base64, sizeof(base64));
}Complete setup instructions are provided in the Installation section above, including:
- Installation methods (Library Manager, manual)
- Prerequisites
- ESP32 board setup
- Required dependencies
- Quick start examples
- Troubleshooting guide
solduino.h (Core SDK)
├── Includes: rpc_client.h
├── Includes: crypto.h, keypair.h
├── Includes: transaction.h, serializer.h
└── Provides: Constants, Version Info
rpc_client.h (RPC Module)
├── Depends on: WiFiClientSecure (ESP32)
├── Depends on: HTTPClient (ESP32)
└── Depends on: ArduinoJson
keypair.h (Wallet Module)
├── Depends on: crypto.h
└── Provides: Keypair management
transaction.h (Transaction Module)
├── Depends on: crypto.h
└── Used by: serializer.h
serializer.h (Serializer Module)
├── Depends on: transaction.h
└── Provides: Transaction serialization
- Modularity: Each module is self-contained with clear interfaces
- Arduino Compatibility: Follows Arduino library conventions
- ESP32 Optimized: Uses ESP32-specific features (WiFiClientSecure)
- Memory Efficient: Designed for embedded constraints
- Easy to Use: Simple API with comprehensive examples
- Create header file (e.g.,
new_module.h) - Create implementation file (e.g.,
new_module.cpp) - Include in
solduino.hunder "Module Includes" section - Update README.md with documentation
- Add example usage
- Add method declaration to
RpcClientclass inrpc_client.h - Implement using
makeRpcRequest()inrpc_client.cpp - Add to README.md API reference
- Update example sketch if applicable
Version is managed in solduino.h:
SOLDUINO_VERSION_MAJOR- Breaking changesSOLDUINO_VERSION_MINOR- New featuresSOLDUINO_VERSION_PATCH- Bug fixes
Version string format: "MAJOR.MINOR.PATCH"
- Add WebSocket support for real-time subscriptions
- Add certificate validation for production use
- Add secure key storage support
- Add more instruction builders (token transfers, program interactions)
- Add transaction simulation support
- Add account data parsing utilities
See examples/basic_rpc_demo/basic_rpc_demo.ino for a complete example.
#include <WiFi.h>
#include <solduino.h>
RpcClient client(SOLDUINO_DEVNET_RPC);
void setup() {
// ... WiFi setup ...
client.begin();
// Get account info
String accountInfo = client.getAccountInfo("11111111111111111111111111111112");
Serial.println(accountInfo);
// Get balance
String balance = client.getBalance("11111111111111111111111111111112");
Serial.println(balance);
}See examples/wallet_demo/wallet_demo.ino for a complete example.
#include <solduino.h>
void setup() {
Serial.begin(115200);
// Generate new keypair
Keypair keypair;
if (keypair.generate()) {
char address[64];
keypair.getPublicKeyAddress(address, sizeof(address));
Serial.println("New wallet address: " + String(address));
// Sign a message
String message = "Hello, Solana!";
uint8_t signature[64];
if (keypair.sign(message, signature)) {
// Verify signature
bool isValid = keypair.verify((uint8_t*)message.c_str(), message.length(), signature);
Serial.println("Signature valid: " + String(isValid));
}
}
}See examples/transaction_demo/transaction_demo.ino for a complete example.
#include <WiFi.h>
#include <solduino.h>
RpcClient client(SOLDUINO_DEVNET_RPC);
Keypair sender, receiver;
void setup() {
// ... WiFi setup ...
client.begin();
// Generate keypairs
sender.generate();
receiver.generate();
// Get blockhash
String blockhashStr = client.getLatestBlockhash();
// Parse blockhash...
uint8_t blockhash[32];
// Create transfer transaction
Transaction tx;
uint8_t fromPubkey[32], toPubkey[32];
sender.getPublicKey(fromPubkey);
receiver.getPublicKey(toPubkey);
tx.addTransferInstruction(fromPubkey, toPubkey, 1000000); // 1 SOL
tx.setRecentBlockhash(blockhash);
// Sign transaction -- private key never leaves the Keypair object
tx.sign(sender);
// Serialize and send
char serializedTx[2048];
if (TransactionSerializer::encodeTransaction(tx, serializedTx, sizeof(serializedTx))) {
String result = client.sendTransaction(String(serializedTx));
Serial.println("Transaction sent: " + result);
}
}void monitorNetwork() {
String health = client.getHealth();
String slot = client.getSlot();
String version = client.getVersion();
Serial.println("Health: " + health);
Serial.println("Slot: " + slot);
Serial.println("Version: " + version);
}Use these examples for DePIN-style data publishing workflows:
examples/sensor_to_chain_demo/sensor_to_chain_demo.ino- Generic templateexamples/temperature_thermistor_demo/temperature_thermistor_demo.ino- Thermistorexamples/temperature_thermocouple_demo/temperature_thermocouple_demo.ino- Thermocouple (MAX6675)examples/temperature_dht22_demo/temperature_dht22_demo.ino- DHT22 temperature + humidityexamples/air_mq135_demo/air_mq135_demo.ino- MQ-135 air sensorexamples/gps_neo7m_demo/gps_neo7m_demo.ino- NEO-7M GPS with TinyGPSPlus
RpcClient(const String& endpoint)Connection Management
bool begin()- Initialize RPC clientvoid end()- Clean up resourcesvoid setTimeout(int timeout)- Set request timeout (ms)
Account Operations
String getAccountInfo(const String& publicKey)- Get account informationString getBalance(const String& publicKey)- Get account balance
Network Information
String getVersion()- Get Solana versionString getSlot()- Get current slotString getBlockHeight()- Get block heightString getHealth()- Check network healthString getLatestBlockhash()- Get latest blockhash (recommended)String getRecentBlockhash()- Get recent blockhash (deprecated, use getLatestBlockhash instead)
Transaction Operations
String sendTransaction(const String& transaction, const String& encoding = "base58")- Send transaction (returns signature, or empty string on error)bool getTransaction(const String& signature, TransactionResponse& tx)- Get transaction details
Block Operations
String getBlock(uint64_t slot)- Get block informationString getBlockCommitment(uint64_t slot)- Get block commitmentString getBlocks(uint64_t startSlot, uint64_t endSlot)- Get multiple blocks
Token Operations
String getTokenAccountsByOwner(const String& owner, const String& mint)- Get token accountsString getTokenSupply(const String& mint)- Get token supply
Utility
String callRpc(const String& method, const String& params)- Custom RPC call
Keypair()Key Generation and Import
bool generate()- Generate a new random keypairbool importFromPrivateKey(const uint8_t* privateKeyBytes)- Import from 64-byte private keybool importFromPrivateKeyBase58(const char* privateKeyBase58)- Import from Base58 private keybool importFromSeed(const uint8_t* seed)- Import from 32-byte seed
Key Retrieval
bool getPublicKey(uint8_t* output) const- Get public key as bytes (32 bytes)bool getPrivateKey(uint8_t* output) const- Get private key as bytes (64 bytes)bool getPublicKeyAddress(char* address, size_t addressLen) const- Get public key as Base58 addressbool getPrivateKeyBase58(char* output, size_t outputLen) const- Get private key as Base58 string
Signing and Verification
bool sign(const uint8_t* message, size_t messageLen, uint8_t* signature) const- Sign messagebool sign(const String& message, uint8_t* signature) const- Sign string messagebool verify(const uint8_t* message, size_t messageLen, const uint8_t* signature) const- Verify signature
Utility
bool isInitialized() const- Check if keypair is initializedvoid clear()- Clear keypair (zero out keys)
Transaction()Transaction Building
bool addTransferInstruction(const uint8_t* from, const uint8_t* to, uint64_t amount)- Add SOL transfer instructionbool addInstruction(const uint8_t* programId, const uint8_t* accounts[], uint8_t accountCount, const uint8_t* data, uint16_t dataLength)- Add custom instructionbool setRecentBlockhash(const uint8_t* blockhash)- Set recent blockhash (32 bytes)
Transaction Signing (prefer the Keypair-based overloads — the private key never leaves the object)
bool sign(const Keypair& signer)- Sign with a singleKeypair. Clears any existing signatures`.bool partialSign(const Keypair& signer)- Sign with a singleKeypairwithout clearing other signatures. Use for multi-party / offline multisig. Mirrorstx.partialSign(payer).bool sign(const Keypair* const signers[], uint8_t count)- Sign with an array ofKeypairpointers. Clears first, then applies each in order.bool sign(const uint8_t* privateKey, const uint8_t* publicKey)- Low-level / legacy: sign with raw key bytes.bool signMultiple(const uint8_t* privateKeys[], const uint8_t* publicKeys[], uint8_t count)- Low-level / legacy: sign with raw key bytes for multiple signers.
Transaction Information
Message& getMessage()- Get transaction messageuint8_t getSignatureCount() const- Get number of signaturesbool getSignature(uint8_t index, uint8_t* signature) const- Get signature by indexbool isValidTransaction() const- Check if transaction is validvoid reset()- Reset transaction
Serialization
static bool serializeMessage(const Message& message, uint8_t* buffer, uint16_t bufferLen, uint16_t& serializedLen)- Serialize message to wire formatstatic bool serializeTransaction(const Transaction& transaction, uint8_t* buffer, uint16_t bufferLen, uint16_t& serializedLen)- Serialize transaction to wire format
Encoding
static bool encodeTransaction(const Transaction& transaction, char* output, size_t outputLen)- Encode transaction to Base64static bool encodeTransactionBase58(const Transaction& transaction, char* output, size_t outputLen)- Encode transaction to Base58
Size Calculation
static uint16_t calculateMessageSize(const Message& message)- Calculate message sizestatic uint16_t calculateTransactionSize(const Transaction& transaction)- Calculate transaction size
static size_t encode(const uint8_t* data, size_t dataLen, char* output, size_t outputLen)- Encode bytes to Base64static size_t decode(const char* input, uint8_t* output, size_t outputLen)- Decode Base64 to bytes
Mainnet:
const String MAINNET_RPC = "https://api.mainnet-beta.solana.com";Devnet:
const String DEVNET_RPC = "https://api.devnet.solana.com";Testnet:
const String TESTNET_RPC = "https://api.testnet.solana.com";client.setTimeout(15000); // 15 secondsHTTP Error -1 (Connection Failed)
- Ensure WiFi is connected
- Check RPC endpoint URL is correct
- Verify HTTPS is properly configured (should be automatic)
HTTP Error -5 (Connection Lost)
- Network instability
- RPC endpoint might be unavailable
- Try increasing timeout:
client.setTimeout(30000)
Compilation Errors
- Ensure ESP32 board support is installed
- Verify ArduinoJson library is installed
- Check all required headers are included
WiFi Connection Issues
- Verify SSID and password are correct
- Check WiFi signal strength
- Ensure 2.4GHz network (ESP32 doesn't support 5GHz)
- ✅ ESP32 (tested)
- ✅ ESP32-S2
- ✅ ESP32-S3
- ✅ ESP32-C3
⚠️ ESP8266 (may require modifications)- ❌ Standard Arduino (insufficient memory)
- Certificate validation is currently disabled for HTTPS (uses
setInsecure()) - Large JSON responses may require increased buffer sizes
- Memory constraints limit transaction size on some devices
- Real-time WebSocket subscriptions not yet implemented
-
Certificate Validation: The library currently uses
setInsecure()which disables SSL certificate validation. For production use, implement proper certificate validation. -
Private Keys: Never expose private keys in your code. Consider using secure storage solutions for production applications.
-
Network Security: Always use HTTPS endpoints. Avoid sending sensitive data over unencrypted connections.
-
Key Management: Implement proper key management practices. Consider hardware security modules (HSM) for production deployments.
Contributions are welcome! Please read our Contributing Guidelines and Code of Conduct before submitting a Pull Request.
We appreciate all kinds of contributions:
- 🐛 Bug reports
- 💡 Feature suggestions
- 📝 Documentation improvements
- 🔧 Code contributions
- ⭐ Star the repository
See CONTRIBUTING.md for detailed guidelines.
This project is licensed under the Apache License 2.0 - see the LICENSE file for details.
- Solana Labs for the Solana blockchain
- ArduinoJson contributors
- ESP32 Arduino Core team
For issues, questions, or contributions:
- GitHub Issues: Create an issue
- Email: parvat.raj2@gmail.com
- Repository: https://github.com/torrey-xyz/solduino
- First stable release
- Complete RPC, wallet, and transaction SDK baseline
- Instruction builder API (
Instruction,AccountMeta,Transaction::add()) - Program helpers (
SystemProgram,TokenProgram) and PDA derivation support - Expanded sensor-to-chain demo suite (thermistor, thermocouple, DHT22, MQ-135, GPS)
- Updated architecture/docs for sensor integrations and setup
Made with ❤️ for the Solana and Arduino communities