コメントでgre_gorが言ったこととは別に(整数を読み書きするには、2バイトを送受信する必要があります)、バイトを送受信するためのより強力な方法が必要です(単にそれらが同期されるのを待って祈ることはオプションではありません)。
適切なプロトコルを作成する方法はたくさんあります。理想的なプロトコルでは、データの開始および/または終了を決定する方法と、送信されているデータを決定する方法が必要です。
ASCII 表現を使用する場合 (つまり、print
とprintln
を使用)、印刷できない文字、スペース、または改行を使用してこれを設定できます。ただし、を使用しているためwrite
、より「バイトセーバー」な手法を使用します。
バイト指向の送信では、通常、これに対処する 2 つの方法を見つけました。
1 つ目は、予約済みバイトと「エスケープ」バイトのセットを使用することです。予約済みのバイトを 1 つ取得するたびに、エスケープを入れてから、そのバイトを変更して非予約に設定します。たとえば、7D と 7E を予約済みとして定義し、7E をエスケープ バイトとして定義した場合 (たとえば、7D は伝送の終わりであり、8 の 7 を変更して予約済みを非予約済みにします)、バイトが配列が 01 34 7D 66 7E の場合、7D を 7E 8D (エスケープ + 非予約 =、7E を 7E 8E に変換) に変換し、7D を追加します。このようにして、01 34 7E 8D 66 7E 8E 7D を送信します。パーサーは7D を受信し、7E を受信するたびにそのバイトを破棄し、次の 8x を 7x に変更することを知っています。
これは、より堅牢なプロトコルを作成する簡単な方法ですが、出力が可変長になるという欠点があります。8 ビット (またはその倍数) よりも短いデータを送信する必要がある場合によく使用する代替方法は、バイトがデータかコマンドかを示すためにビットを予約し、残りの 7 ビットを使用して実際のデータを格納することです。 . あなたが送信している2つの変数が署名されており、範囲が16383から-16384であると仮定して、私はあなたのケースでこのアプローチを使用しています。
したがって、プロトコルは単純です。受信したバイトの最初のビットが 1 の場合、これはコマンドです。有効なコマンドは次のとおりです。
0x80 Accelerometer data
0x81 LDR data
(you can add your own ones, but all of them should start with a 1 bit)
すべてのコマンドの後に 2 つのデータ バイトが続き、各データ バイトは 0 ビットで始まります。構成は
bit position 7 6 5 4 3 2 1 0
1st byte '0' b13 b12 b11 b10 b09 b08 b07
2nd byte '0' b06 b05 b04 b03 b02 b01 b00
ここで、'0' は 0 ビットで、bxx は送信されるデータの xx 番目のビットです。
これで、データの送信は簡単になりました。この単純なコードはそれを行うことができます:
#define SEND_ACCEL_DATA 0x80
#define SEND_LDR_DATA 0x81
void sendData(uint8_t type, int value)
{
mySerial.write(type);
mySerial.write((value >> 7) & 0x7F); // Upper 7 bytes
mySerial.write(value & 0x7F); // Lower 7 bytes
// Never needed to call flush, but if you need it put it here
}
usage:
sendData(SEND_LDR_DATA, ldr_value);
sendData(SEND_ACCEL_DATA, accelerom_value);
ご覧のとおり、型の後に値を送信しているだけです。
受信コードも単純です。
#define SEND_ACCEL_DATA 0x80
#define SEND_LDR_DATA 0x81
if (mySerial.available() >= 3)
{
switch (mySerial.read())
{
case SEND_ACCEL_DATA:
{
int acceleration = mySerial.read() & 0x7F;
acceleration = (acceleration << 7) | (mySerial.read() & 0x7F);
if (acceleration & 0x2000) acceleration |= 0xC000; // This is the sign extension
// now you can use acceleration (and store it in the database)
}
break;
case SEND_LDR_DATA:
{
int ldr = mySerial.read() & 0x7F;
ldr = (ldr << 7) | (mySerial.read() & 0x7F);
if (ldr & 0x2000) ldr |= 0xC000; // This is the sign extension
// now you can use ldr (and store it in the database)
}
break;
}
}
必要に応じて、これを関数に埋め込むこともできます。
#define SEND_NONE 0x00
#define SEND_ACCEL_DATA 0x80
#define SEND_LDR_DATA 0x81
int getDataFromSerial(uint8_t *p_type)
{
int result = 0;
*p_type = SEND_NONE;
if (mySerial.available() >= 3)
{
switch (*p_type = mySerial.read())
{
case SEND_ACCEL_DATA:
case SEND_LDR_DATA:
int result = mySerial.read() & 0x7F;
result = (result << 7) | (mySerial.read() & 0x7F);
if (result & 0x2000) result |= 0xC000; // This is the sign extension
break;
default:
*p_type = SEND_NONE;
break;
}
}
return result;
}
// Usage
uint8_t type;
int result = getDataFromSerial(&type);
switch (type)
{
case SEND_ACCEL_DATA:
// result is the acceleration
break;
case SEND_LDR_DATA:
// result is the ldr
break;
}
両方のデータを同時に受信するのではなく、順番に受信することに注意してください。データを同時に受信する必要がある場合は、両方のデータを同時に送受信できるように変更したバージョンをアップロードします。
編集:尋ねられたように、ここにデータが一緒に移動するバージョンがあります。
ここで、2 つのデータをグループ化すると、パケットのタイプは 1 つだけになります。したがって、バイトの種類を示すバイトは必要ありません。したがって、バイトが開始バイトかどうかを報告するようにプロトコルを変更できます。プロトコルには、4 バイト長の 1 種類のパケットしかありません。
bit position 7 6 5 4 3 2 1 0
1st byte '1' l13 l12 l11 l10 l09 l08 l07
2nd byte '0' l06 l05 l04 l03 l02 l01 l00
3rd byte '0' a13 a12 a11 a10 a09 a08 a07
4th byte '0' a06 a05 a04 a03 a02 a01 a00
ここで、l13-l00 は LDR 値の 14 ビットで、a13-a00 は加速度のビットです。
送信は変更されていますが、何を送信するかがわかれば簡単です。
void sendData(int ldr, int accel)
{
mySerial.write(((ldr >> 7) & 0x7F) | 0x80); // Upper 7 bytes and first bit
mySerial.write(ldr & 0x7F); // Lower 7 bytes
mySerial.write((accel >> 7) & 0x7F); // Upper 7 bytes
mySerial.write(accel & 0x7F); // Lower 7 bytes
}
usage:
sendData(ldr_value, accel_value);
注目に値する唯一のことは、最初のバイトに最高ビットが設定されているのに対し、他のバイトには設定されていないことです。
受信側のコードは、4 バイトすべてが到着するのを待ち、最初のバイトが実際に 1 で始まることを確認してから、前の方法とまったく同じように 2 つの値を復元するだけです。
while (mySerial.available() >= 4)
{
if (!(mySerial.peek() & 0x80))
{ // If the first byte in the buffer is not a valid start, discard it
mySerial.read();
continue;
}
int ldr = mySerial.read() & 0x7F;
ldr = (ldr << 7) | (mySerial.read() & 0x7F);
if (ldr & 0x2000) ldr |= 0xC000; // This is the sign extension
int acceleration = mySerial.read() & 0x7F;
acceleration = (acceleration << 7) | (mySerial.read() & 0x7F);
if (acceleration & 0x2000) acceleration |= 0xC000; // This is the sign extension
// now you can use acceleration and ldr (and store them in the database)
}
}
関数が必要な場合は、次の方法で実装できます。
boolean getDataFromSerial(int *p_ldr, int *p_accel)
{
boolean foundData = false;
while ((!foundData) && (mySerial.available() >= 4))
{
if (!(mySerial.peek() & 0x80))
{ // If the first byte in the buffer is not a valid start, discard it
mySerial.read();
continue;
}
*p_ldr = mySerial.read() & 0x7F;
*p_ldr = ((*p_ldr) << 7) | (mySerial.read() & 0x7F);
if ((*p_ldr) & 0x2000) (*p_ldr) |= 0xC000; // This is the sign extension
*p_accel = mySerial.read() & 0x7F;
*p_accel = ((*p_accel) << 7) | (mySerial.read() & 0x7F);
if ((*p_accel) & 0x2000) (*p_accel) |= 0xC000; // This is the sign extension
foundData = true;
}
}
return foundData;
}
usage:
int accel, ldr;
if (getDataFromSerial(&ldr, &accel))
{
// use accel and ldr
}
この場合、2 つの変数は常に一緒に送信されますが、もう一方の変数では別々に送信できます。どちらが良いですか?必要なものによって異なります。これはより簡単で効果的ですが、もう一方はより柔軟です。
編集 2:完全な例:
最新のプロトコル (2 つのデータを一緒に) で通信する 2 つの arduino の完全な例を設計しました。左側のものは 2 つのポテンショメータから始まる 2 つの値を生成し、もう一方はそれらを出力します。
ここで見つけることができます:https://circuits.io/circuits/3690285-software-serial-binary-transmission
接続:
送信者コード:
#include <SoftwareSerial.h>
SoftwareSerial mySerial(10, 11);
void sendData(int ldr, int accel)
{
mySerial.write(((ldr >> 7) & 0x7F) | 0x80); // Upper 7 bytes and first bit
mySerial.write(ldr & 0x7F); // Lower 7 bytes
mySerial.write((accel >> 7) & 0x7F); // Upper 7 bytes
mySerial.write(accel & 0x7F); // Lower 7 bytes
}
// the setup routine runs once when you press reset:
void setup() {
Serial.begin(9600);
mySerial.begin(2400);
}
// the loop routine runs over and over again forever:
void loop() {
int ldr_value = analogRead(A0);
int accel_value = analogRead(A1);
sendData(ldr_value, accel_value);
// Print it on the serial interface for debug
Serial.print("TX LDR: ");
Serial.print(ldr_value);
Serial.print(" - ACC: ");
Serial.println(accel_value);
delay(1000);
}
受信者コード:
#include <SoftwareSerial.h>
SoftwareSerial mySerial(10, 11);
boolean getDataFromSerial(int *p_ldr, int *p_accel)
{
boolean foundData = false;
while ((!foundData) && (mySerial.available() >= 4))
{
if (!(mySerial.peek() & 0x80))
{ // If the first byte in the buffer is not a valid start, discard it
mySerial.read();
continue;
}
*p_ldr = mySerial.read() & 0x7F;
*p_ldr = ((*p_ldr) << 7) | (mySerial.read() & 0x7F);
if ((*p_ldr) & 0x2000) (*p_ldr) |= 0xC000; // This is the sign extension
*p_accel = mySerial.read() & 0x7F;
*p_accel = ((*p_accel) << 7) | (mySerial.read() & 0x7F);
if ((*p_accel) & 0x2000) (*p_accel) |= 0xC000; // This is the sign extension
foundData = true;
}
return foundData;
}
// the setup routine runs once when you press reset:
void setup() {
Serial.begin(9600);
mySerial.begin(2400);
}
// the loop routine runs over and over again forever:
void loop() {
int accel, ldr;
if (getDataFromSerial(&ldr, &accel))
{
// Print it on the serial interface for debug
Serial.print("RX LDR: ");
Serial.print(ldr);
Serial.print(" - ACC: ");
Serial.println(accel);
}
}