So far I've used this protocol for a smart fridge and a bilirubin measurement device (for testing Jaundice). Because popular Arduino Bluetooth interfaces happen to act as media independent serial lines, this will also work with Bluetooth. http://www.electronica60norte.com/mwfls/pdf/newBluetooth.pdf
Responses are returned in the 'value' or 'error' parameters during reads. The 'value' parameter is used as the source during writes.
import serial
import struct
import time
params = ('option', 'pin', 'value', 'error')
ser = serial.Serial('COM3') # opening a serial connection to arduino over USB will reset the device
ser.read(1) # wait for ready signal so we know that arduino came back online.
ser.write(struct.pack('<hhhh', 0, 0, 0, 0))
response = struct.unpack('<hhhh', ser.read(8))
print(dict(zip(params, response))
/*
* ComPacket Data Structure
* >option: 0 = Analog Read, 1 = Digital Read, 2 = Analog Write, 3 = Digital Write
* >pin: pin to read/write from
* >value: Arduino will return pin value to this variable. Leave empty on request
* >error: Arduino will return an Error code is there's a problem with the request, or reading a pin value
*/
enum ComPacket_option {A_READ, D_READ, A_WRITE, D_WRITE};
enum ComPacket_error {INVALID_OPTION, INVALID_READ_PIN, INVALID_WRITE_PIN};
struct ComPacket {
enum ComPacket_option option;
int pin;
int value;
enum ComPacket_error error;
} buf;
void ComPacket_zero(ComPacket *packet) {
packet->option = (enum ComPacket_option) 0;
packet->pin = 0;
packet->value = 0;
packet->error = (enum ComPacket_error) 0;
}
void ComPacket_handleRead(ComPacket *packet) {
if (packet->option == A_READ) {
packet->value = analogRead(packet->pin);
return;
}
if (packet->option == D_READ) {
packet->value = digitalRead(packet->pin);
return;
}
}
void ComPacket_handleWrite(ComPacket *packet) {
if (packet->option == A_WRITE) {
analogWrite(packet->pin, packet->value);
return;
}
if (packet->option == D_WRITE) {
digitalWrite(packet->pin, packet->value);
return;
}
}
void ComPacket_handleError(ComPacket *packet, enum ComPacket_error error) {
ComPacket_zero(packet);
packet->error = error;
}
void ComPacket_processPacket(ComPacket *packet) {
switch (packet->option) {
case A_READ:
case D_READ: ComPacket_handleRead(packet); break;
case A_WRITE:
case D_WRITE: ComPacket_handleWrite(packet); break;
default: ComPacket_handleError(packet, INVALID_OPTION);
}
}
/*
* ******************* END COMPACKET
*/
// Use Serial1 for TX/RX pins
/*
* https://www.arduino.cc/reference/tr/language/functions/communication/serial/
* Serial, Serial1, Serial2 and Serial3 are available. See reference for which you need
*/
auto SerialDevice = Serial;
void setup() {
// put your setup code here, to run once:
ComPacket_zero(&buf);
SerialDevice.begin(9600,SERIAL_8N1);
// wait for serial port to connect. Needed for native USB
while (!SerialDevice);
SerialDevice.write(1);
}
void loop() {
// check if data available
if (SerialDevice.available() > 0) {
// if so process packet and send back result
SerialDevice.readBytes((char*)&buf, sizeof(ComPacket));
ComPacket_processPacket(&buf);
SerialDevice.write((char*)&buf, sizeof(ComPacket));
SerialDevice.flush();
}
}