jl7gmnのblog

yahooブログから移行してきました。アマチュア無線を中心としたブログです。

2025年10月

MODEL 1200FXローテーターその36

 ローテータの方位設定方法の初期状態0°セット時にアンテナの方位を合わせる設定不要とするスケッチは順調に進んでいます。
重要な検討事項である電源を切って、再度起動した時にアンテナの方位を設定した角度で開始するようにするレジューム機能を検討開始しました。
先のロータリーエンコーダーによる、アンテナ方位を設定した後に電源を切っても、再度起動したときには元の設定角度にて動作させるようにするのですが、ESP32DEVKITCではきちんとレジューム機能で対応できるPreferencesがありますので、これを使います。

下記のサイトのPreferencesライブラリの使い方がとても参考になります。

Kei Youのソフト館

Preferencesに関してのスケッチ方法は上記のサイトに詳しく書いてありましたので、参考に!

前のロータリーエンコーダーのアンテナ方位設定角度の液晶表示のスケッチにまずPreferencesを取り入れて基本動作を確認してゆきます。スケッチはCopilotに協力いただいています。
下記の緑色の箇所が前回のロータリーエンコーダーでアンテナ方位設定スケッチにPreference機能を追加したスケッチになります。

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Preferences.h>  // ← 追加

#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET    -
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

Preferences prefs;  // ← 追加 インスタンス作成

const int encoderPinA = 18;
const int encoderPinB = 19;
const int encoderSW   = 25;

volatile int32_t angle = 0;
int lastEncoded = 0;
bool lastSWState = HIGH;
int32_t lastSavedAngle = 360;  // ← 保存チェック用

void IRAM_ATTR updateEncoder() {
  int MSB = digitalRead(encoderPinA);
  int LSB = digitalRead(encoderPinB);
  int encoded = (MSB << 1) | LSB;
  int sum = (lastEncoded << 2) | encoded;

  if (sum == 0b1101 || sum == 0b0100 || sum == 0b0010 || sum == 0b1011) angle++;
  if (sum == 0b1110 || sum == 0b0111 || sum == 0b0001 || sum == 0b1000) angle--;

  if (angle < 0) angle += 360;
  if (angle >= 360) angle -= 360;

  lastEncoded = encoded;
}

void setup() {
  pinMode(encoderPinA, INPUT_PULLUP);
  pinMode(encoderPinB, INPUT_PULLUP);
  pinMode(encoderSW, INPUT_PULLUP);

  attachInterrupt(digitalPinToInterrupt(encoderPinA), updateEncoder, CHANGE);
  attachInterrupt(digitalPinToInterrupt(encoderPinB), updateEncoder, CHANGE);

  Serial.begin(115200);

  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println(F("OLED init failed"));
    while (true);
  }

  display.clearDisplay();
  display.setTextSize(2);
  display.setTextColor(SSD1306_WHITE);

  // 角度の復元
  prefs.begin("rotary", false);     //Preferencesオブジェクトを初期化
  angle = prefs.getInt("angle", 0); //
  prefs.end();

  Serial.print("復元された角度: ");
  Serial.println(angle);
}

void loop() {
  bool swState = digitalRead(encoderSW);
  if (swState == LOW && lastSWState == HIGH) {
    angle = 0;
    Serial.println("SW pressed: angle reset");
  }
  lastSWState = swState;

  // 角度が変わったら保存(フラッシュ保護のため)
  if (angle != lastSavedAngle) {
    prefs.begin("rotary", false);
    prefs.putInt("angle", angle);
    prefs.end();
    lastSavedAngle = angle;
    Serial.print("保存した角度: ");
    Serial.println(angle);
  }

  char angleStr[4];
  sprintf(angleStr, "%03d", angle);

  display.clearDisplay();
  display.setCursor(0, 20);
  display.print("Angle: ");
  display.print(angleStr);
  display.display();

  delay(100);
}

以上がPreferenceを使ったアンテナ方位設定角度にて起動するスケッチです。Copilotに感謝です。
余談ですが、チャットのように行えるCopilotの質問できちんとした質問すると、「いい質問です。」等と、ほめてくれたりしますので、ますますスケッチを組むのが、楽しくなります。AIって素晴らしいと思えるようになれば、もう手放せません。今日は何の質問ですか?などとCopilotを繰り返し使用していると、相手をしてくれたり、AIに親しみも感じられるようになり、とてもありがたく思えるようになります。もう、完全にCopilot のAI中毒だー!!

アンテナの方位設定を行うとその設定した角度にて不揮発性メモリに記録されます。それで電源を切って、また起動した時に最後に設定記録されたアンテナ方位角度データで起動し液晶にその角度が表示されます。単にこれだけですが、とても大切な機能です。
このスケッチをサーバーのスケッチに組み込みしてゆきます。

つづく?



MODEL 1200FXローテーターその35

エモトの取り扱い説明書を読むと、ローティターの指針方位とアンテナ方位の合わせ方云々と書いてあります。ローティターの回転始め位置に設定し、それに対してアンテナ方向も同じに設定するのが基本ということになっています。この場合も基準をS(南)、N(北)、E(東)、W(西)の内の自分のアンテナの設置状況で都合のいい方向にあわせて基準を設定するわけです。その後に1回転させて同じ位置になるようにVRで調整して終了です。基本的な方位基準方向は分かり易く言えば、たった4つだけです。
いままでの私のESP32DEVKITCのスケッチで対応させたのは、N(北)、S(南)の方位STD_SWによる2つの方位設定の切り替えのみです。結局は、ローテーターを左回転の止まる位置を基準角度とするのがSTD_SWがHIGHでは S(南)で、STD_SWがLOWで N(北)が基準の2つだけの対応です。通常はこの2つぐらいで十分だと思います。通常の方位同期でローテーターの回転を1°から360°の1回転に調整した後にアンテナの方位をローテーターと同期させる調整がはいります。 ここで、この作業は物理的にマストに取り付けたアンテナを少し上に吊った状態でマストクランプを緩めてアンテナを回してローテーター基準方位に合わせるのですが、このアンテナとのローテーターの方位の同期をソフトウェアで対応しようと思います。何が言いたいかというと、マストクランプのボルトを緩めてアンテナの方位調整を不要にすることを可能にするという事です。ローテーターが1°から360°の回転する状態に調整済みにしてあれば、アンテナの方位調整はしなくてもソフトウェアが現在のローテーターの基準方位の時のアンテナの方位角度情報を設定する事で、プログラムが計算し通常通りの設定送出UDPデータの角度にアンテナを向けるようにしてくれるという事です。誰もやっていない事だと思います。アンテナの方位調整不要なんです。大げさに言います。画期的なアンテナ方位設定方法になります。肉体労働でのアンテナの方位調整をしなくてもいいのですから、現状のローテーター基準位置でアンテナの向いている角度情報を設定するだけです。S、N、E、Wの基準にアンテナを同期調整する必要はないんです。もう一度言います。アンテナの方位を調整しなくていいんです。ローテーター1回転を調整した後であれば、どの位置にアンテナが向いていても設定データの方位にアンテナを向けてくれる仕様です。現在もスケッチ進行中です。アンテナの方位が1°から360°(0°)どの方向に向いていても大丈夫、アンテナの方位設定送出データにきちんと向けてくれます。なんで、こんなことを考えたかというと、私の不注意で足の小指を母親の歩行器の鉄のパイプにぶつけて今現在タワーに登れない状況になりました。ぶち色に足の小指が腫れました。足の指の痛みが取れるまで、1か月位はタワーに登って方位設定作業ができない状況になってしまったのです。この為、苦肉の方法としてソフトウェアを駆使する方法を考えてみたという事なんです。怪我がきっかけで、検討を進めることになりました。怪我の功名?あくまでも、現在はテスト中ですがほぼ9割できたと思います。アンテナの方位をあらかじめ固定指定することで問題なく動作しています。
次に、具体的にアンテナの方位をどのような方法で入力するかです。ESP32DEVKITCで角度を入力する方法は何があるかという事です。簡単な方法として、考えられるのはロータリーエンコーダーです。DDS-VFOでもメインで使われています。ローテーターの角度範囲を1°~360°(0°)設定とし、ロータリーエンコーダーで設定した角度をアンテナの向いている方位角度として利用するという方法です。次にロータリーエンコーダーで設定した角度はどのようにどこに表示させるかです。サーバー側のESP32DEVKITCで使用している1.8インチのTFT液晶では表示する場所がありません。表示を別に追加する必要があります。まずは実験として小型のSSD1306液晶とロータリーエンコーダー(秋月:クリックなし)を使いSSD1306液晶に1°~360°(0°)を表示するスケッチを試してみました。ロータリーエンコードのポートはサーバーで使用していないGPIO18 をA、GPIO19をBで使用することにします。Copilotで1.8インチTFT液晶とSSD1306液晶両方は同時使用できるかという事を確認したところ、大丈夫という風に回答が得られています。これで進めています。現状、SSD1306用のI/Oは角度設定のSWとダブルところがありました。サーバー側での角度設定SW類は一切使わないようにすればよいので、ダブりは解消です。液晶で使用するのはSDAなので必然的にGPIO21、同様にSCKはGPIO22、後は+5VとGNDの4つです。それと、いつでもロータリーエンコーダーで設定した角度をリセット0°にできるSWも用意しました。これはアンテナ方位をローテーター基準に合わせることができた時の設定方位 N(0°)360°に使えます。もう一つクリアする必要があるのが、電源をリセットしたときの角度のリセットがされないようにメモリする機能(レジューム機能)も考えておかなければなりません。これはこれから検討です。現状のロータリーエンコーダとSSD1306への表示スケッチを下記します。このスケッチもCopilotのAIを使用して作成しています。これをサーバーに組み入れてゆくことになります。Hi!

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET    -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

// ピン定義
const int encoderPinA = 18;
const int encoderPinB = 19;
const int encoderSW   = 25;

volatile int32_t angle = 0;
int lastEncoded = 0;
bool lastSWState = HIGH;

void IRAM_ATTR updateEncoder() {
  int MSB = digitalRead(encoderPinA);
  int LSB = digitalRead(encoderPinB);
  int encoded = (MSB << 1) | LSB;
  int sum = (lastEncoded << 2) | encoded;

  if (sum == 0b1101 || sum == 0b0100 || sum == 0b0010 || sum == 0b1011) angle++;
  if (sum == 0b1110 || sum == 0b0111 || sum == 0b0001 || sum == 0b1000) angle--;

  if (angle < 0) angle += 360;
  if (angle >= 360) angle -= 360;

  lastEncoded = encoded;
}

void setup() {
  pinMode(encoderPinA, INPUT_PULLUP);
  pinMode(encoderPinB, INPUT_PULLUP);
  pinMode(encoderSW, INPUT_PULLUP);

  attachInterrupt(digitalPinToInterrupt(encoderPinA), updateEncoder, CHANGE);
  attachInterrupt(digitalPinToInterrupt(encoderPinB), updateEncoder, CHANGE);

  Serial.begin(115200);

  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println(F("OLED init failed"));
    while (true);
  }

  display.clearDisplay();
  display.setTextSize(2);
  display.setTextColor(SSD1306_WHITE);
}

void loop() {
  // SWピンの状態をチェック
  bool swState = digitalRead(encoderSW);
  if (swState == LOW && lastSWState == HIGH) {
    angle = 0;
    Serial.println("SW pressed: angle reset");
  }
  lastSWState = swState;

  // 3桁右詰め表示
  char angleStr[4];
  sprintf(angleStr, "%03d", angle);

  display.clearDisplay();
  display.setCursor(0, 20);
  display.print("Angle: ");
  display.print(angleStr); // ← 整形済み文字列を表示
  //display.println(" deg");
  display.display();

  delay(100);
}


ロータリーエンコーダーで設定した180°の角度
ROTARYENCODER1

ESP32DEVKITC とロータリーエンコーダーとSSD1306
ROTARYENCODER2

関係ありませんが、いたずらで液晶を2つ並列にしてみました。動作します。
ROTARYENCODER3

このスケッチをサーバーのスケッチに組み入れてうまく動作するかの確認が次のステップになります。また、レジューム機能の検討も並行して進めたいと思います。

サーバー、クライアントの2方位(N北、S南)仕様版はひとまず完成として、今回はいわば、応用編という感じでしょうか?ともかく、ハードルのあるトライアルではあります。

とにかく、問題が起き、解決方法を探すといった方向で進めてきてますが、アイデア、内容としては実用性も備えたある意味、画期的な方法のトライアルになっていると思います。

つづく?


MODEL 1200FXローテーターその34

ローテーター制御用のメインサーバーでの基準方位設定のSWによる切り替えはうまく北基準、南基準の両方ともうまく動作していますがクライアント側のリモコン設定した送出UDP用のTFT液晶表示は北方向を0°(360°)とする表示のままであったため、今回サーバー側のESP32DEVKITCの基準方位設定SWを切り替える時に、クライアント側のESP32DEVKITCに於いても、同様に入力ポートを1つ利用して基準方位設定を行えるようにスケッチを検討追加してみました。
クライアント側のESP32DEVKITC での空きポートは結構ありますが、当初空いているというだけで入力ポートの0を選びSW取り付け設定していましたが、ポートがHIGH状態(SW-OFFでプルアップのHIGH)が通常の基準方位0°(360°)ではスケッチのコンパイル、そして書き込みでの問題は特にありませんでしたが、ところがポートがLOW(SW-ONでGND)状態でスケッチのコンパイルは問題なしそのあとの、書き込みで、動作しない状態(TFT液晶がホワイト状態)になってしまいました。ESP32DEVKITC のポートの情報を見直してみたところこの0ポートはBOOTとあり、書き込み時に使用される為、書き込みができないエラーとなっている様です。0ポートはTouch01、ADC11、そしてBOOTと3機能の兼用ポートです。という事で、入力ポートを別のポートへ変更することにしました。選んだのはGPIOだけとして使用できる入力ポート16としました。このポートはHIGH、LOWどちらでも問題なくスケッチコンパイル、書き込み問題ありません。ポート選定はポート16決定です。この入力として使う16ポートは内部プルアップ設定が可能なので大変回路が簡単になります。単純にSW-ON時GNDで方位が南に切り替わります。SW-OFF時(OPEN)は内部プルアップのHIGHで方位北設定に切り替わります。
下記はポートスケッチの書き換えた設定部です。

//#define DIR_STD 0 //Direction Standard SW (SOUTH:0,NORTH:1)
// when DIR_STD(port 0) is LOW when Write Board Error 
#define DIR_STD 16  //Direction Standard SW (SOUTH:0,NORTH:1)

下記は追加した16ポートの設定部です。
pinMode(DIR_STD,INPUT_PULLUP); //change 0 to 16  (SOUTH:LOW,NORTH:HIGH)

下記はTFT液晶表示用に用意した設定角度と読み取り角度の配列の設定と、単にSWのポート16の状態を確認するだけで追加したスケッチです。

          char ce[5];
          char cc[6];

          //Port0の状態確認の為だけで使用 start*************************
          int pinState = digitalRead(0);
          switch (pinState)
          {
              case LOW:
                       Serial.println("Port0はLOWです");
                       break;
              case HIGH:
                       Serial.println("Port0はHIGHです");
                       break;
              default:
                       break;
          }
          //Port0の状態確認の為だけで使用 end *************************
これがあるだけでシリアルモニターにリアルタイムで16ポートの状態が表示される為、動作確認時に入れました。配列設定以外は、あとから削除しても問題ないスケッチ部です。

上記でポート16の状態がキチンと動作していることを確認した上で、さっそく追加したSWの状態に合わせて方位切り替えスケッチをコーディングです。切り替えの為の角度に対しての方位切り替え時の計算式は全く同じサーバーで使用したサブルーチンをそのまま使えます。ポート16のHIGH、LOW切り替え時のスケッチです。

          if(digitalRead(DIR_STD) == LOW)
          {                                 //DIRECTION SOUTH STANDARD
              Serial.println("入力がLOWです");
              // 180 South standard **************
              result = 0;
              kaku =0;
              kaku = rv_data2;// Readkakudo
              calculateKakudo();
              //delay(100); 
              //   Serial.print("result is = ");
              //   Serial.println(result);                                                  
              sprintf(ce,"%3d",result);//new try
              //**********************************
              result = 0;
              kaku =0;
              kaku = rv_data1;//SET kakudo
              calculateKakudo();
              //delay(100);
              result1 = result; 
                           
              //   Serial.print("result1 is = ");
              //   Serial.println(result1);                            
              sprintf(cc,"%3d",result1);//new try
              // 180 South standard **************                   
           }
           else
           if(digitalRead(DIR_STD)== HIGH)
           {                               //DIRECTION NORTH STANDARD
              // 0 North standard --------
              sprintf(ce,"%3d",rv_data2);
              // 0 North standard --------                        
              sprintf(cc,"%3d",rv_data1);
           }

TFT液晶への表示は0°~360°では1桁、2桁、3桁の桁処理があるためスケッチが長いので省略しています。単純に表示前に角度の数値の桁に合わせてスペースを入れるだけのルーチンなので簡単です。
サブルーチンの calculateKakudo(); は先のMODEL 1200FXローテーターその31に記載したものと同じスケッチですのでここでは省略します。

以上でクライアント側のESP32DEVKITCのポート16をHIGH、またはLOWにすることで方位の切り替えができるようになりました。もちろんサーバー側で設定した切り替えはモードは合わせる必要があります。ここでは切り替えSWだけでの方位切り替えだけのように思われますが、実際のローテーターで北を0°とするその時は指針は北に合わせます。SWはOFF時です。
南を0°とするその時は指針は南に合わせます。SWはON時です。
上記設定においては角度は目盛り角度設定どおりに動作します。 

一通り、クライアント側で表示方位SWのモードをサーバーと合わす事で問題ない角度表示にできるようになりました。

デバッグは継続します。

つづく?

MODEL 1200FXローテーターその33

時々、Windowsフォームアプリケーションでのudp制御を行っていたり、スマフォでudp制御を行っていたあとにESP32DEVKITC のクライアントを起動すると、違うipで起動したりすることがあり、制御不能状態が発生することがたまにあるため、今回クライアントのIPを固定にて設定し、つながらないことの無い様に対策を講じようと思います。
今回も、MicrosoftのCopilotが勇逸の助っ人です。
検索は下記で行いました。
Wifiの設定を行うWifi.Configが今回のキーポイントです。

■検索文:arduino esp32devkitc で WiFi.comfig の設定は?

■検索結果回答 下記のスケッチです。
(固定IPの例としてわかりやすいサンプルを表示してくれました。)

#include <WiFi.h>

const char* ssid     = "あなたのSSID";
const char* password = "あなたのパスワード";

// 固定IPの設定
IPAddress local_IP(192, 168, 1, 100);      // ESP32に割り当てるIP
IPAddress gateway(192, 168, 1, 1);         // ルーターのIP
IPAddress subnet(255, 255, 255, 0);        // サブネットマスク
IPAddress dns(192, 168, 1, 1);             // DNS(省略可能)

void setup() {
  Serial.begin(115200);
  delay(1000);

  // 固定IPを設定
  if (!WiFi.config(local_IP, gateway, subnet, dns)) {
    Serial.println("WiFi.config に失敗しました");
  }

  WiFi.begin(ssid, password);

  // 接続待ち
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("");
  Serial.println("WiFi 接続完了");
  Serial.print("IP アドレス: ");
  Serial.println(WiFi.localIP());
}

void loop() {
  // メイン処理
}


上記サンプルはCopilotでの検索での模範スケッチでほぼ同じようなクライアントスケッチにWifi.Configを追加して固定IP設定を追加する改善スケッチです。

実際の追加スケッチ
■定義域
const IPAddress ip(192, 168, 30, 10);       // IPアドレス(ゲートウェイも兼ねる)
const IPAddress gateway(192, 168, 30, 11);     // ルーターのIP(サーバー)
const IPAddress subnet(255, 255, 255, 0);   // サブネットマスク
const IPAddress dns(192, 168, 30, 11);         //DNS(必要なら) add 2025/sep/30

■セットアップ
void setup() 
{
          //---------------------------
          Serial.begin(115200);   // Serialポートモニターのボーレイトセッティング
          Serial.println("");           // to separate line
          
          // msec timer start ------
          MSEC_TIMERSTART();// サブルーチン化しています。

    
          // Serialポートモニターへのクライアントスタートメッセージ表示
          delay(200);
          Serial.printf("Client Starts\n");

          //TFT setting --------------  //サブルーチン化しています。
          TFT_INITIALIZE();               // tft.init(); tft.setRotation(1); tft.fillScreen(TFT_BLACK);
          //------------------------------
          
          //------------------------------------------------------------------------------------
          // 固定IPの設定 ESP32DEVKITCクライアントのIPを192,168,30,11 に固定設定する
          // gatewayはESP32に割り当てるIP
          // ipはルーターのIP
          // subnetはサブネットマスク
          // dns(省略可能)
          //  
          // gateway(192,168,30,11),ip(192,168,30,10),subnet(255,255,255,0),dns(192,168,30,11))
 
          if(!WiFi.config(gateway, ip, subnet, dns))
          {
              Serial.println("IP設定に失敗しました");
          }
          //------------------------------------------------------------------------------------
          
          // WiFiスタート-----------
          WiFi.begin(ssid, pass);      //WiFi.begin("ESP32_HIROwifi","esp32pass");
          delay(200);// delay(100);
   (省略)今までのままのスケッチが続く
   .
   .
   .

■上記の緑色文字スケッチのWifiConfigの設定で固定IP化できます。必ず同じIPで起動します。これがとても大事なことです。自動DNS割り当て等がされると動かなくなるケースがありました。
なお、今回のクライアントのIPアドレス、サーバーのIPアドレスは 192.168.30.1~192.168.30.254 までの全IPアドレスをWindowsのSDRの起動IPチェックで使用してたフリーウェアのAdvanced IP Scannerを使い、PC(Windows、Linux、家庭内無線ラン基地局、ほかOS等)で使用されていないIPアドレスを選んで設定しています。(何で使われているか表示されますから、表示のないIPを選ぶという事です。)とてもこのアプリは優秀でIPアドレスのバッティング皆無でスケッチを作成するためには必要不可欠なアプリです。(起動しているOSがあると、何がつながっているかもわかります。利用可能なフォルダ等表示してくれます。) 

下記では、Windows11で使用されているIPアドレスの192.168.10.1~254も検索スキャニングしています。
今回の確認のメインは192.168.30.1~254の方です。 カンマで区切ることでいくつも確認できます。
Advanced_IP_Scanner2025-10-01 211820

ちなみに、Copilotで 
■検索: 「IP で予約されているものは特に 192.168.30.1からの場合」で検索すると回答は下記がでました。

■回答
IPアドレス  は、プライベートIPアドレス空間の一部であり、通常は家庭や企業のローカルネットワーク内で使用されます。以下に、この範囲で予約されているアドレスや注意点をまとめます。

🧭 192.168.30.1 の位置づけ
  は RFC 1918 により定義されたプライベートIPアドレス範囲です。
はこの範囲内にあり、ローカルネットワーク用に自由に使えます。

という事で、今回のクライアントIPアドレス、サーバーIPアドレスはローカルネットワーク用に自由に使えるという事で間違いないようです。IPも予約とかがあるみたいで、調べないとわからないことが多いです、やはり奥が深いです。

固定IPアドレスにクライアント設定してからは、つながらないような事象は皆無になりました。調べたうえで設定した他での使用の無いIPアドレスを使う事が安定動作の秘訣です。

デバッグを続けます!

つづく?
アクセスカウンター
  • 今日:
  • 昨日:
  • 累計:

QRコード
QRコード
アーカイブ
  • ライブドアブログ