サーバ

import processing.net.*; //ネットワークライブラリの使用宣言

String RecvMsg,zouka;

float  RecvData; // 受信用のfloat型変数

int      state   = 0;    // サーバの受信状態

PImage   RecvImg; //受信画像を格納するPImage型変数

int imgIndex = 0;

int cnt =0;

int state1=0;

//最大クライアント数
final int MAX_CLIENT = 2; //定数

Server[] myServer = new Server[MAX_CLIENT]; //サーバクラスの宣言
String[] ServerIP = new String[MAX_CLIENT]; //サーバIP表示用文字列

Client[] myClient = new Client[MAX_CLIENT]; //クライアントクラスの宣言
String[] ClientIP = new String[MAX_CLIENT]; //クライアントIP表示用文字列

String[] Message = new String[MAX_CLIENT]; //クライアントごとの表示メッセージ用文字列

int numClient; //接続クライアント数

//******************************************************************************
// 初期設定関数
//******************************************************************************
void setup( )
{
  size( 600, 500 );
  
  //日本語フォント(BIZ UDゴシック)で、サイズ16のフォントを生成
  PFont font = createFont("BIZ UDゴシック", 16, true);
  textFont(font); // 生成したフォントを設定

  //サーバの起動(クライアント毎にポート番号を5554と6554にしました.適当笑)
  myServer[0] = new Server(this, 5554); //クライアントA用にします.
  myServer[1] = new Server(this, 6554); //クライアントB用にします.
  
  for( int i = 0; i < MAX_CLIENT; i++ ){
    ServerIP[i] = myServer[i].ip( ); // 自分のIPアドレスを文字列に記録
    ClientIP[i] = " "; // クライアントIPアドレス表示用文字列の初期値
    Message [i] = " "; // メッセージの初期値
  }

  RecvMsg  = " ";
  
  RecvData = 0.0f; // 初期値
  
  numClient = 0; //接続クライアント数を0にリセット
  
}

void stop( ){
  for( int i = 0 ; i < MAX_CLIENT; i++ )
     myServer[i].stop( );
}

void draw( ) // 画面描画(100ms毎に勝手に繰り返す)
{
  background(#086C52); // 緑に塗りつぶす.
  
  //左上にクライアントと自分のIPアドレスを表示
  for( int i = 0 ; i < MAX_CLIENT; i++ ){
    //左上から下に向かって,サーバと自IPアドレスを表示
    text("サーバ   : " + ServerIP[i], 30, 20 + i * 100 ); 
    text("クライアント: " + ClientIP[i], 30, 40 + i * 100 );
    text(Message[i]                     , 30, 60 + i * 100 );
  }
  
  text( RecvMsg  , 30, 60 );
  text("荷物数"+cnt,70,80);
  
  //受信文字列を表示
  text("受信データ:" + nf(RecvData,1,2), 50, 100 );
  
  
   // 受信画像をウィンドウに表示
  if( RecvImg != null ) 
    image(RecvImg, 45, 200, RecvImg.width * 0.6, RecvImg.height * 0.5); 

}

//******************************************************************************
// クライアント接続時に起動されるイベント
//******************************************************************************

void serverEvent( Server ConServer, Client ConClient )
{ 
  String[] ClientID = {"A", "B"}; //クライアントの名前(表示用)
  
  //クライアントがどのポートから接続してきたかチェック
  //ポートに対応するクライアントを表(配列)に登録.
  
  for( int i = 0; i < MAX_CLIENT; i++ ){
    if( ConServer == myServer[i] ){
      myClient[i] = ConClient;
      ClientIP[i] = ConClient.ip( );
      Message [i] = "クライアント" + ClientID[i] + "と接続";
      numClient++; //接続クライアント数を+1
    }
  }
}
/*
//*************************************************************************
//データを受信したら起動されるイベント(引数は送信先の情報)
//*************************************************************************
int    ImgWidth     = 0;    // 受信画像の幅
int    ImgHeight    = 0;    // 受信画像の高さ
int    NumBytes     = 0;
  
void clientEvent(Client RecvClient){ 
  
  //String RecvDataString;
 
  NumBytes = RecvClient.available( ); //受信データのバイト数を確認
     
  if( NumBytes>=1 && NumBytes<=2  ){ //受信データがあったら
    
    println("増加を検知s");
  }
  
   // 受信状態に応じた動作の記述(状態0:画像の高さと幅の受信, 状態1:画像データの受信)
  switch(state){
    //--------------------------------------------------------------------------------------------------
    case 0 : // 画像の高さと幅を受信
    //--------------------------------------------------------------------------------------------------
      //NumBytes = RecvClient.available( ); //受信データのバイト数を確認
      
      if( NumBytes >= 8 ){ //8バイト分、データを受信したら
        byte[] TmpBuff = RecvClient.readBytes(NumBytes); //今受け取ったバイトデータを一時バッファに保存
        
        ImgHeight  = (TmpBuff[3] & 0xff) << 24 | (TmpBuff[2] & 0xff) << 16 | (TmpBuff[1] & 0xff) << 8 | TmpBuff[0] & 0xff;
        ImgWidth   = (TmpBuff[7] & 0xff) << 24 | (TmpBuff[6] & 0xff) << 16 | (TmpBuff[5] & 0xff) << 8 | TmpBuff[4] & 0xff;
        Message[2] = "高さ= " + ImgHeight + ", 幅= " + ImgWidth;
        state      = 1; //状態遷移
        myServer.write(1); // 高さと幅の受信完了をクライアントに通知
        println("a");
      }
      break;
     
      
    //--------------------------------------------------------------------------------------------------
    case 1 : // 画像データの受信(総画素数×RGBの3バイト)
    //--------------------------------------------------------------------------------------------------
      
      if( NumBytes >= ImgHeight * ImgWidth *3 ){ //全画像データ(RGB×高さ×幅)を受信したら
        
        Message[2] += " => 受信完了: " + NumBytes + "[バイト]";
        
        byte[] TmpBuff = RecvClient.readBytes(NumBytes);//受信バッファ内の全画像データを読み出す。
        
        //画像を格納するメモリ領域を確保
        println(ImgWidth);
        println(ImgHeight);
        RecvImg = createImage(ImgWidth, ImgHeight, RGB);
        int idx = 0;
        for( int i = 0; i < ImgWidth * ImgHeight; i++ ){
          //画素を詰め込む。
          RecvImg.pixels[i] = color(( TmpBuff[idx+0] & 0xff), ( TmpBuff[idx+1] & 0xff), ( TmpBuff[idx+2] & 0xff));
          idx += 3;
        }
        RecvImg.updatePixels( ); //領域のアップデート
        
        String savedFileName = "Image" + imgIndex + ".jpg";
        RecvImg.save(savedFileName);
        Message[2] += "⇒保存" + savedFileName;
        
        imgIndex++;
        state = 0;   //状態遷移
        myServer.write(300); // 画像データの受信完了をクライアントに通知
        println("aa");

      }
      break;
      
    default : break;
  }

}*/

// 画像を受け取る関数
void clientEvent(Client client) {
  
  byte[] receivedData = client.readBytes(); // クライアントからバイトデータを受信
  
  if (receivedData != null && receivedData.length > 10000) {
    String fileName = "Received_Image_" + imgIndex + ".jpg"; // 受信画像のファイル名を生成
    saveBytes(fileName, receivedData); // 受信データを保存
    RecvImg = loadImage(fileName); // 保存した画像を読み込む
    byte[] fileData = loadBytes(fileName); // ファイルをバイト配列として読み込む、消すかも
    
    byte[] senddata={1,1};//長さ2
    myServer[1].write(senddata);//消すかも
    cnt=0;

    if (RecvImg != null) {
      println("画像を受信し保存しました: " + fileName);
      imgIndex++;
      Message[1] = "画像を受信し保存しました: " + fileName;
    } else {
      println("画像の読み込みに失敗しました: " + fileName);
      Message[1] = "画像の読み込みに失敗しました: " + fileName;
    }
  } 
  
  //試し
  else if (receivedData != null && receivedData.length >= 1) {
    String receivedText = new String(receivedData).trim(); // バイトデータを文字列に変換して空白を削除

    // 入力が数値かどうかを確認
    if (isNumeric(receivedText)) {
        int imageIndex = Integer.parseInt(receivedText); // 数値に変換
        String fileName = "Received_Image_" + imageIndex + ".jpg"; // ファイル名を生成
        
            println("画像を送信: " + fileName);
            byte[] fileData = loadBytes(fileName); // ファイルをバイト配列として読み込む
            myServer[1].write(fileData); // クライアントBに送信
    } else {
    println("画像データが無効です。");
    Message[1] = "画像データが無効です。";
    println("増加");
    cnt++;
    myServer[1].write(1);//荷物の増加を通知
  }
  }    

  //試し
  
  
  
  else {
    println("画像データが無効です。");
    Message[1] = "画像データが無効です。";
    println("増加");
    cnt++;
    myServer[1].write(1);
  }
}

//******************************************************************************
// データ受信時に起動されるイベント
//******************************************************************************
/*void clientEvent( Client ConClient )
{ 
  RecvMsg = "クライアント(" + ConClient.ip( ) + ")と接続";
  // クライアントから画像データを受信
    receiveImage(ConClient);   
    cnt=0;
  }
}*/

boolean isNumeric(String str) {
  try {
    Integer.parseInt(str);  // 数値変換を試みる
    return true;
  } catch (NumberFormatException e) {
    return false;  // 変換できない場合はfalseを返す
  }
}

クライアントA(マイコン)

import processing.net.*; //ネットワークライブラリの使用宣言
import processing.serial.*;
import processing.video.*; // 動画ライブラリの使用宣言
import gab.opencv.*;

Serial Myport;

int numRecvByte = 0;

int [] ADIntVal = new int [2];
float [] ADRealVal = new float[2];

byte [] RecvByteBuff = new byte[256];

Client   myClient; //クライアントクラスclientの宣言

float  SendData; //送信するfloat型の変数

float val0 = 0;//増加検知用の変数

int state1 = 0;//状態

int state2=0;

String[] FileName = new String[4]; // 画像ファイル名を格納する文字列配列

String[] Message = new String[2];  // メッセージ用文字列

String Time;

PImage[] img = new PImage[4]; // イメージ型配列imgを宣言

Capture video; // Captureオブジェクトの宣言

OpenCV opencv;

int numImg = 0; // 送信する画像のインデックス

int state = 0;

float val1=0;

//******************************************************************************
// 初期設定関数
//******************************************************************************
void setup( ){
  
  size( 1000, 700 ); // ウィンドウを生成
  // 日本語フォント(BIZ UDゴシック)で、サイズ16のフォントを生成
  PFont font = createFont("BIZ UDゴシック", 16, true);
  textFont(font); // 生成したフォントを設定

  // サーバーに接続(localhostは自パソコン)
  myClient = new Client( this, "localhost", 5554 ); 
    
  SendData = 0.0f; // 初期値
  
  int i;
  for (i = 0; i < Serial.list().length; i++){
    println(Serial.list()[i]);
  }
  println("ARE available");
  
  Myport = new Serial(this, Serial.list()[i-1], 115200);
  
  // カメラの設定(デバイス番号0、解像度640x480)
  video = new Capture(this, 640,480);
  opencv = new OpenCV(this, 640,480);
  video.start();
  
  Message[0] = Message[1] = " "; // メッセージの初期値
}

PImage binImg;

void draw( ) //画面描画(100ms毎に起動)
{
  background(#FF7F50); //オレンジに塗りつぶす.
  Myport.write(0);
  
  String[] cameras = Capture.list();
  while( cameras.length == 0){
    cameras = Capture.list();
  }
  
  
  //左上にクライアントと自分のIPアドレスを表示
  text( "クライアント: " + myClient.ip(), 30, 20 );
  
  //送信データを表示
  text("電圧値 =" + nf(ADRealVal[0],1, 2) + "[V]",30,40);
  
  Time = year() + "年" + month() + "月" + day() + "日" + hour() + ":" + minute() + ":" + second();
  
  text(Time,10,60);
  
  if(video != null){
    set(10,130,video);
    opencv.loadImage(video);   
  }
  
  if(val0==0 && state2==1){
    takepic();
    sendimg();
    state2=0;
    println("aas");
  }
  else if(val0>0){
    state2=1;
    
    println("hhs");
  }
  
}

void captureEvent(Capture camera){
  camera.read();
}

//電圧値計算
/*String SendDataString;
int calVol(){
  //String SendDataString;

  RecvByteBuff[numRecvByte] = (byte) RecvPort.read();
  numRecvByte++;
  
  if(numRecvByte == 4){
    
    ADIntVal[0] = RecvByteBuff[1] << 8 |(RecvByteBuff[0] & 0xff);
   
    ADRealVal[0] = ((ADIntVal[0] / 1023.0) * 5.0);
    
    numRecvByte = 0;
      
  //float型を文字列に変換
  
  if(ADRealVal[0] - val0 > 0.2 && state1 == 0){
    
    //重さが増えたら
    SendDataString = str(ADRealVal[0]);
    val0 = ADRealVal[0];
    state1 = 1;
    delay(200);
    return 0;
 
    //サーバに文字列を送信
    //myClient.write(SendDataString);
    
  }
 
  else if(ADRealVal[0]  == 0.0 && state1 == 1){
    val0 = 0;
    SendDataString = str(ADRealVal[0]);
    state1 = 0;
    delay(200);
    return 1;
    //myClient.write(SendDataString);  
  }   
  }
  return 2;
}*/

void serialEvent (Serial RecvPort){
  String SendDataString;
  
  RecvByteBuff[numRecvByte] = (byte) RecvPort.read();
  numRecvByte++;
  
  if(numRecvByte == 4){
    
    ADIntVal[0] = RecvByteBuff[1] << 8 |(RecvByteBuff[0] & 0xff);
   
    ADRealVal[0] = ((ADIntVal[0] / 1023.0) * 5.0);
    
    numRecvByte = 0;
      
  //float型を文字列に変換
  
  if(ADRealVal[0] - val0 > 0.2){
    
    //重さが増えたら
    SendDataString = str(ADRealVal[0]);
    val0 = ADRealVal[0];
    state1 = 1;
    myClient.write(1);
    delay(400);
 
    //サーバに文字列を送信
    //myClient.write(SendDataString);
    
  }
 
  if(ADRealVal[0]  == 0.0 && state1 == 1){
    val0 = 0;
    SendDataString = str(ADRealVal[0]);
    state1 = 0;
    delay(200);
    
    //myClient.write(SendDataString);   
  }   
  }
}

// 写真を撮影
void takepic() {
    PImage capturedFrame = video.get(); 
    String savedFileName = "Image.jpg";
    capturedFrame.save(savedFileName); // カメラのフレームを保存
    
    // 保存した画像を読み込み配列に格納
    img[numImg] = loadImage(savedFileName);
if (img[numImg] == null) {
  println("画像の読み込みに失敗しました: " + savedFileName);
} else {
  println("画像が正常に読み込まれました: " + savedFileName);
  Message[1] = "写真を撮影し保存し、送信準備完了: " + savedFileName;
}  
  
}
/*
//画像を送信
int ImgHeight = 0;
int ImgWidth = 0;
byte[] SendImgData;

void sendimg() { 
  int NumBytes = 2;

  switch (state) {
    case 0: // 画像の高さと幅の送信
      //NumBytes = c.available();
      if (NumBytes >= 1) {
        //c.clear(); // 受信バッファを空にする。
        
        // 画像の高さと幅を送信(それぞれint型4バイト)
        ImgHeight = img[numImg].height;
        ImgWidth = img[numImg].width;
        byte[] SendHW = new byte[8]; // 送信用バッファ
        
        // 画像の高さを送信バッファに詰める。
        SendHW[0] = (byte) ImgHeight;
        SendHW[1] = (byte)(ImgHeight >> 8);
        SendHW[2] = (byte)(ImgHeight >> 16);
        SendHW[3] = (byte)(ImgHeight >> 24); 
        // 画像の幅を送信バッファに詰める。
        SendHW[4] = (byte) ImgWidth;
        SendHW[5] = (byte)(ImgWidth >> 8);
        SendHW[6] = (byte)(ImgWidth >> 16);
        SendHW[7] = (byte)(ImgWidth >> 24);
        
        myClient.write(SendHW); // バッファをサーバに送信
        state = 1; // 状態遷移
        println("画像のサイズを送信しました");
      }
      break;
    
    case 1: // 画像データの送信
      //NumBytes = c.available();
      //if (NumBytes >= 1) {
        // // 受信バッファを空にする。
        
        // 送信バッファの動的確保(総画素数×RGBの3バイト分)
        SendImgData = new byte[ImgWidth * ImgHeight * 3];
        int idx = 0;
        // 画像データを全部、送信バッファに詰め込む。
        for (int i = 0; i < ImgWidth * ImgHeight; i++) {
          color pix = img[numImg].pixels[i]; // 画像データから1画素を取り出す。
          SendImgData[idx + 0] = (byte) red(pix); // 画素の赤成分を送信バッファに詰める。
          SendImgData[idx + 1] = (byte) green(pix); // 画素の緑成分を送信バッファに詰める。
          SendImgData[idx + 2] = (byte) blue(pix); // 画素の青成分を送信バッファに詰める。
          idx += 3;  
        }
        myClient.write(SendImgData); // 送信バッファをサーバに送信
        //state = 0; // 状態遷移
        println("画像データを送信しました");
      //}
      break;
      
    case 2: // サーバーから画像データの受信完了が来るまで待つ
      //NumBytes = c.available();
      println(NumBytes);
      if (NumBytes >= 1) {
        //c.clear(); // 受信バッファを空にする。
        Message[1] = FileName[numImg] + " : 画像送信の完了 " + SendImgData.length + "[バイト]";
        state = 0;
      }
      break;
  }
}

void clientEvent(Client c){
  int NumBytes1=c.available();
  if(NumBytes1==1){
    c.clear();
    state=1;
  }
  
  if(NumBytes1==2){
    c.clear();
    state=0;
  }
}*/

// 写真を送信
void sendimg() {
    if (img[numImg] != null) {
        String fileName = "Image.jpg"; // 送信するファイル名
        byte[] fileData = loadBytes(fileName); // ファイルをバイト配列として読み込む

        if (fileData != null) {
            myClient.write(fileData); // クライアントにファイルデータを送信
            println("画像を送信しました: " + fileName);
            Message[1] = "画像を送信しました: " + fileName;
        } else {
            println("画像データの読み込みに失敗しました: " + fileName);
            Message[1] = "画像データの読み込みに失敗しました: " + fileName;
        }
    } else {
        println("送信可能な画像がありません。");
        Message[1] = "送信可能な画像がありません。";
    }
}

クライアントB

import processing.net.*;
import java.util.ArrayList;

String[] Message = new String[3];
int state = 0; // サーバの受信状態
PImage RecvImg; 
int imgIndex = 0;
String Time;
ArrayList<String> logs = new ArrayList<>(); // ログを保持するリスト

Client myClient; //クライアントクラスclientの宣言

void setup() {
  size(860, 580);

  PFont font = createFont("BIZ UDゴシック", 16, true);
  textFont(font);

  myClient = new Client(this,"localhost", 6554);

  Message[0] = "サーバ:" + myClient.ip();
  Message[1] = " ";
  Message[2] = " ";
}

void stop() {
  myClient.stop();
}

void draw() {
  background(#2D3986);
  
  fill(255);
  text(Message[0] , 30, 20);
  //text(Message[1], 100, 30);
  text(Message[2], 30, 40);
  Time = year() + "年" + month() + "月" + day() + "日" + hour() + ":" + minute() + ":" + second();
  
  // ログを画面に表示
  fill(255);
  int logY = 60; // ログの表示開始位置
  for (int i = 0; i < logs.size(); i++) {
    text(logs.get(i), 30, logY + i * 20); // 各ログを表示
  }

  
  text(Time,200,20);

  // 受信画像をウィンドウに表示
  if( RecvImg != null ) 
    image(RecvImg, 45, 200, RecvImg.width * 0.6, RecvImg.height * 0.5); 
}

void serverEvent(Server s, Client c) {
  Message[1] = "クライアント(" + c.ip() + ")と接続";
}

// 画像を受け取る関数
void clientEvent(Client client) {
  //int NumBytes=0;
  //NumBytes = client.available();
  byte[] receivedData = client.readBytes(); // クライアントからバイトデータを受信

  if(receivedData.length==1){
     Message[2] = "荷物が届いています";
  }
  else if (receivedData != null && receivedData.length > 8) {
    String fileName = "Received_Image_" + imgIndex + ".jpg"; // 受信画像のファイル名を生成
    saveBytes(fileName, receivedData); // 受信データを保存
    RecvImg = loadImage(fileName); // 保存した画像を読み込む

    if (RecvImg != null) {
      println("画像を受信し保存しました: " + fileName);
      Message[2] = "荷物は空です";
      imgIndex++;
      Message[1] = year() + "年" + month() + "月" + day() + "日" + hour() + ":" + minute() + ":" + second()+"に画像を受信し保存しました: " + fileName;
      addLog(Message[1]); 
  } else {
      println("画像の読み込みに失敗しました: " + fileName);
      Message[1] = "画像の読み込みに失敗しました: " + fileName;
    }
  } else {
    println("画像データが無効です。");
    Message[1] = "画像データが無効です。";
}
}
/*
int NumBytes=0;
//******************************************************************************
// データ受信時に起動されるイベント
//******************************************************************************
void clientEvent(Client ConClient ){ 

  NumBytes = ConClient.available();
  if(NumBytes>=8){
  // クライアントから画像データを受信
    receiveImage(ConClient);
    Message[2] = "荷物は空です";
    
  }
  else{
     Message[2] = "荷物が届いています";
  }
  
}*/

void addLog(String log) {
  logs.add(log); // 新しいログを追加
  if (logs.size() > 20) { // 最大20件まで表示
    logs.remove(0); // 最古のログを削除
  }
}