레이블이 MCU-ESP32인 게시물을 표시합니다. 모든 게시물 표시
레이블이 MCU-ESP32인 게시물을 표시합니다. 모든 게시물 표시

1/10/2024

ESP TinyML (Edge AI)

1. Edge AI (TinyML)  

TinyML을 Meetup or Linkedin을 통해 여러번 보기만 하고, 관련자료들을 거의 잘 정리하지 않아, 
아래와 같이 간단히 정리하도록 한다. 
우선 TinyML 을 알기전에 기본적인 Machine Learning 과 Deep Learning을 좀 알아보고 
Tensorflow or Edge Impulse를 사용해보는게 맞을 것 같다. 


ESP32/ARM 기반의 TinyML
TinyML 관련된 부분 내용이며, 개발 Tool 과 Inference를 위한 NN network 관련자료 

Tensorflow/Tensorboard 
Tensorflow 와 Tensorboard 기본사용법 과 분석방법  (오래되어 많이 까먹음) 

MNIST 모델의 기본이해 
가장 기본인 MNIST 모델을 이해

CNN(Convolutional Neural Network) 관련자료 
Convolution Network 예전에 정리한 자료 

Jupiter/Colab 사용법 
기본 Jupyter(Colab) 사용법 


1.1 Edge AI/TinyML 개발 Tool 

Edge Impulse or Tensorflow Lite를 사용방법은 서로 비슷하리라고 본다. 
아직 Edge Impulse는 사용해보지 못하고 매번 글만 읽어 보기만 했으며, 관련 Tool들만 간단히 소개한다. 

TensorFlow Lite for Microcontrollers
TinyML 기반으로 진행할 경우, Tensorflow 와 Tensorboard로 진행 

Edge Impuse 
TinyML Meetup에서 알게되었으며, 이걸로도 많이 진행하며, 주로 보면 Tensorflow와 비슷하리라 본다. 

TensorFlow Lite for ESP32 
ESP32 의 Tensorflow 관련자료 


1.2 Edage AI Use Case 와 Cloud 

Edge AI, TinyML을 주로 많이 사용하는 것 같으며, ARM 뿐만 아니라 ESP32 시리즈 각 CPU 아키텍쳐마다 
이를 지원을 해주는 것으로 보인다. 

  • Edge AI Use Case
요즘 Edge AI , TinyML은 거의 들어갈 것으로 보이며, 핵심을 주로 보면, Cloud와 조합이다.
Edge는 Edge 일 뿐이며, Cloud와 어떻게 조합할 것이며, 역할을 어디까지 정하는게 중요할 것 같다. 

  • Google Firebase 
Realtime Database를 제공해주고 있으며, 간단한 IoT를 제작하여 실시간으로 확인하고 싶다면 괜찮은 것 같다. 

  • AWS 
AWS는 현재 거의 사용해본게 AWS EC2 와 IoT(S3) 만 사용해봐서 뭐라고 말을 못하겠다.
추후에 AWS를 좀 더 잘 알면, 그때 더 보도록 하자. 특히 Lamda !!! 

  • AWS/GCP/Azure 
Could를 사용해보면, 항상 이곳에서 이 기능을 제공하며, 거의 다 다른 곳에서도 비슷한 기능을 제공해준다.  
나도 Cloud를 좀 더 사용해보고 말을 해야 겠지만, 얼추 제공은 다 해주는 것 같다. 

  • RealTime Chart 관련자료 
이전에 Chart 관련 JavaScript을 Firebase기반으로 Realtime으로 이용한 적 있는데, 추후 이를 다시 이용할때 확인 



2. ESP 기반의 TinyML 분석 

ESP32 or ESP32-S3 기반으로 TinyML을 구성하기 위해서는 기본적으로 아래의 Component기본으로 사용한다.
ARM 과 달리 ESP 경우 DL을 별도로 또 제공해주고 있다. 

ESP-NN(Neural Network)
ARM도 이를 제공하고 있으며, 나중에 Inference할 때 같이 사용 

ESP-DL(Deep Learning Model)
아래를 보면, ESP32-S3기반으로 성능측정된게 있는데, Data Cache만 64KB사용했을 경우이다.

ESP32 Memory (SRAM)
문제는 나중에 SRAM이 될 것 같으며, 이 부분은 별도로 생각을 해야 할 것 같다. 


2.1 Human Activity Recognition Edge AI 

ESP32-S3 기반의 TinyML기반 동작되어진다. 
마지막에 보면 Model을 ESP-DL로 Converting 해서 사용하며, 이는 .cpp 와 hpp로 구성되어진다. 
나중에 직접 실행을 해보면 알겠지 ㅋㅋㅋ (Fine Tuning 도)
 
  
  • Model 구조 
CNN 기반으로 가속도계 센서 기반으로 사람의 행동 파악
  1. Input: 가속도계 센서 (MPU6050)
  2. Output: 7가지 행동패턴 

  • Tensorflow 기반의 CNN 구조 및 설명 
Model을 만들어가면서 설명해주고 있어 괜찮음


  • Espressif Blog (Human Activity Recognition)
상위 내용들은 모두 아래의 Blog 기반으로 작성되어있으며, 아래의 Blog을 읽으면 세부적인 내용이 나온다.  

2.2 Hand Gesture Recognition Edge AI 

ESP32-S3 기반의 TinyML기반 동작되어지며, 상위와 비슷하다.
Jupyter를 Colab을 사용한다고 하며, 나중에 ESP-DL Format으로 Convert해서 사용한다고 하며, 
ONNX로 한다고 하는데, 내가 까먹었다. (CNN는 동일) 

  • ONNX
나중에 시간되면 다시 보면 되겠지 

  • Edge AI H/W Optimzation 과 관련내용 
좋은 설명을 해주고 있는데, 내가 아직 이해를 다 못하겠다. 
  
  • Model 구조 
CNN 기반으로 Model이며, 카메라에서 얻은 것을 ESP32-S3 EYE
  1. Input: Camera Image (ESP32-S3 EYE)
  2. Output: 손가락 Pattern (현재 6개의 Gesture, score기반으로 구분)

Model의 Dataset (Kaggle 것 사용)

  • 관련자료 수집


Espressif Blog (Hand Gesture Recognition)
상위 내용들은 모두 아래의 Blog 기반으로 작성되어있으며, 아래의 Blog을 읽으면 세부적인 내용이 나온다.  


2.3 기타 Edge Impuse 기반 자료

  • Edge Impuse 기반 진행 
ESP32 TinyML 
개발 Tool : Edge Impulse 와 Arduion IDE으로 진행을 한 것으로 보이며, 다양하게 존재하며 
대부분 카메라 중심으로 되어있다. 

6/21/2023

RS485 Modbus-RTU 의 OTA 구현

1. Modbus-RTU 와 SCADA 시스템 

RS485 기반의 Modbus RTU 구조는 이전의 문서들을 참조 하도록 하자. 

RS485 Modbus 와 SCADA 시스템1

RS485 Modbus 와 SCADA 시스템2

Modbus-RTU 기반으로 각 Device들을 구현하면, 최종적으로 이 기반으로 OTA도 별도로 구현을 하고 싶어할 것이다. 
하지만, 안타깝게도, 이것으로, OTA를 해주는 기능은 없으며, 별도로 본인 Protocol 정의해서 구현을 해야한다. 


1.1 Modbus-RTU Memory Map 

우선 Modbus-RTU는 Device에게 Data Read only 와 Read/Write만 제공하는 단순한 Protocol이다. 
이를 좀 더 세부적으로 제어하기 위해서는 별도 Protocol 정의하고 부족한 부분을 채워 각 기능을 보완을 해야한다. 

예를들면, OTA 구현이라든지, Device 세부제어라든지, 이런 요소들을 위해서 각 Memory Map 정의하도록 하자 
  1. Input Register  (Read only 2byte)
    1. Modbus RTU Status 
    2. System Status
    3. Each Event 
    4. System Information 
  2. Holding Register  (Read/Write 2byte)
    1. Modbus RTU Command
    2. Modbus RTU Status 
    3. Modbus RTU Error 
    4. Modbus RTU Argument 
    5. Modbus RTU Buffer
    6. 이외 System Setup 

내가 생각한 것은 간단하게 Command 기반으로 각 상태를 만들어 Device를 구동시키는 방법이다. 
상위와 같이 각 Modbus RTU Command를 각 만들어 Command Driven 방식으로 간단히 device driver를 보완하여 만들어 보자.
유사하다면, MMC가 될 수도 있겟으며, 상위처럼 구현할 경우, 반드시 Master에서 구현하고 테스트를 해봐야 한다. 

  • eMMC/SDCard Interface 
MMC Spec 세부적으로 보면, 각 CMD 의 상태도가 있으며, 이부분과 비슷하다. 


1.2 Modbus-RTU 기반 OTA 

Modbus 가 기본적으로 Request/Response 기반이므로, SCADA(Master), 특정 Register를 Command 기반으로 변경하여 진행하도록 한다.
이에 대한 반응 항상 특정 Resiter를 통해, Deivce Status 체크하는 구조이다.

상위에서 언급한 MMC가 이런 방식으로 동작하며, 다양한 Device Driver들이 이와 유사한다. 
그리고, 각 Command마다 Flow 및 Status가 본인 알아서 정의하도록 하자.  

  • OTA 구현시 문제사항 
OTA를 진행할 경우, Modbus-RTU 에서는 많은 Data를 전송하기 때문에, 여기에도 좀 문제가 발생하는데, 
주로 Cache(CPU Cache가 아니며, Buffer)로 인하여, 중복되는 문제 부분일 것 같다.
이 부분은, OTA 진행 할 경우, 
RS232의 Flow Control 이 존재하지만, RS485에서 크게 의미가 없어서, 별도로, S/W Flow Control Header 정의해서, 이 부분을 보완하였다. 
테스트를 해봤지만, RS232 Flow Control이 RS485로 되면서 별의미가 없고, Modbus-RTU(RS485)는 내부적으로 Flow Control이 없는걸로 생각되어짐 

이 부분은 간단히 정의해서 이 부분을 보완하여 넣도록하자.
예를들면, 각 Offset 과 Size 정의하고, 이를 Master 와 Slave가 각각 확인하는 구조이며, 이외 Buffer 성능 및 에러문제등을 개선하여 만들어 보도록하자. 
이외 다양한 문제가 발생할 수 있으며, 각 상황에 맞추어 본인이 세부적으로 분석해서 대응하자.  

  • 나의 경우는 HW적으로 다음과 같은 문제가 발생 
  1. RS485 Tranceiver 의 오동작 ( Reset 후 발생 문제가 발생)
  2. Master에서 Response를 제대로 못가져오는 문제 
    1. 이 문제가 발생하여 상위 Flow Control 기능추가 
  3. ESP32의 FreeRTOS 문제 및 성능문제 
    1. 이 문제는 S/W 문제가 다양하며, Serial 부분과 많이 연관되어짐
    2. Serial Buffer 이외 FreeRTOS Buffer도 별도로 존재 

일단 나의 경우는 각 OTA Command 와 Status를 별도로 정의하여 구현했으며, 테스트 결과 별 이상이 없다.

더불어, 좀 더 세부적으로 성능 및 OTA 테스트 분석하기 위해서 Python 기반으로 이를 OTA 테스트 하는 Program을 작성하였다. 
또한 Modbus RTU 기능을 간단히 Profiling (RS485 버스 점유율 및 속도)해주는 Program도 별도 추가구현 하였다.

만약, 이를 상용으로 동작되는 프로그램있다면,이를 적극 이용하거나, 그 Tool에 맞게 제작 하도록하자.
구지, 나처럼 다 일일이 만들지 말도록 하자. 

주로 Profiling 하는 부분은 ESP32의 RS485/Modbus-RTU 성능이므로, 이 Bus 점유율(Share-rate)를 각 속도별로 비교해보면 되겠다.
그리고, 각 에러율도 좀 비교해보면 더욱 좋겠다. (이 부분은 만들었지만, 비교하지 않았다)

참고로, ESP32의 경우, 속도가 높아질수록 성능 많이 부족해지는데, 다른 곳에 많은 리소스를 사용해서 그런건지,(아마 이쪽이 가능성이 높다) 
아님 ESP32 문제인지는 나도 아직 세부적으로 분석하지 않았다. 


2. SCADA 에 Modbus RTU 적용문제 

나 역시, 정확한 SCADA를 실제로 접해 본적이 없기에, 이에 적용하는 방법은 본인 정한 Protocol Flow와 Memory Map 을 알려주는 방법 밖에는 없는 거 같다.
나의 경우, Python 기반으로 나만의 Master기반으로 직접 테스트하며 구현했기 때문에, 이 부분은 나중에 한 번 다시 한번 생각해봐야겠다. 


2.1 Python 기반 Unit 테스트 

Modbus 역시, Python 과 NodeJS도 지원해주기 때문에, 본인 작성하기 쉬운 걸로 선택해서 각 테스트 프로그램들을 작성하도록 하자. 
상위에 이미 설명을 했으며, 각 OTA 성능 및 Profile을 위해서 가 부분을 비교 분석하도록 하였다. 

내가 TEST Program을 작성하면서, 어려움이라고 하면, 각 Python Library 찾는 법? 
나머지는, 구글링하면, 대충 작성이 가능하다. 






2.2 VS Code 의 Unit TEST (Automated)

외부 DevOps 처럼, 완전히 Cloud 내부에서 하는 것은 아니지만, 본인 개발환경에서 쉽게 가능하다. 
나의 경우, Github에서는 CircleCI를 이용하지만, VS Code Manaul보면, 외부에서도 아래와 같이 쉽게 본인 마음대로 Power Shell Script 기반으로 구성가능하다. 


VS Code를 이용하여 자동화 
완벽히 Jenkins는 아니여도, 얼추 Jenkins 느낌처럼 구성가능하다. 

1/10/2023

RS485 Modbus 와 SCADA 시스템2

1. ESP32 회로구성 

이전에 ESP32 Modbus 관련하여 정리한 것이 그림 때문에 너무 긴거 같아, 다시 두개로 나누어 정리한다. 
더불어,RS485 와 Modbus를 분리하여, 잘못 안 부분이 있다면, 수정하도록 하겠다. 

RS232 와 RS485 회로구성 (ESP32 회로도 참조)


1.1 RS232 와 RS485 회로구성 

ESP32는 RS232만 지원가능하며, RS232는 Full Duplex이며, RS485는 Half Duplex 이다. 
Modbus를 구성하려면 두가지 방법이 있는데, ESP32에서만 제공하는 회로도만 보고, 이해했다가, 
현재 사용하고 USB제품을 통해 Echo/Non Echo 모드를 알게되었다. 


  • RS485 Mod 구성 (회로도)
  1. Echo Mode: 이전 ESP32 Sample 회로도 with CD(Collision Detection)기능 
  2. Non-Echo Mode: 이전 ESP32 Sample 회로도  without CD(Collision Detection) 기능 

Echo/Non-Echo Mode 관련내용 

문제는 CD(Collision Detection)기능이 있는 Echo Mode를 ESP32에서 완전히 제공하고 있지 않으므로, 주의해햐한다.
동작은 되지만, 멈추는 현상이 발생하며, ESP-IDF를 업그레이드 하면 추후에 제공될 것이겠지만, 현재 내 버전은 그렇지 않다. 


1.2 RS485 Bus 와 다른 Bus 구성 비교  

  • RS485의 Tranceiver 간의 비교 
RS485 Tranceiver VCC마다 다 다를수 있으며, VCC의 Range 정해져 있지, 동일한 Volatage로 사용여부는 정해져 있지 않다. 
이는 아래의 CSMA/CD에서 CD부분에서 자세히 설명하겠다. 

  • Ethernet 와 RS485 Tranceiver 비교  
Etherent과 비교하면, 구성면에서 있어서 비슷한 구성, VCC가 다 동일하며, Volatage 기반으로 Collision Detection이 가능하다  

  • USB 와 RS485 Tranceiver 비교  
USB과 비교하면, USB Master에서 VCC/VDD 나오며 전력도 제어하는 구조로 상위와 완전다르다. 
USB는 Power만 가지고도, 끝도 없이 이야기 할수 있으므로, USB 세부사항들은 아래참고 


1.3 RS485 의 CD(Collision Detection) 기능 

CSMA/CD or CSMA/CA는 대부분 Bus의 Layer 2에서 제공을 해주는 기능이며, 공용 Bus를 같이 사용하기 위해서 사용되는 기술이다.
간단하게 말해, 공용 Bus에서 TX를 하기전에, CS(Carrier Sense) 즉, Bus 이용중인지 확인하고, TX를 하고, 
Collision을 어떻게 감지(Detection)하는지 혹은 피하는 기능(Avoid)이며, 이로 인하여, 여러 Device들이 MA(Multiple Access)하여 같이 통신하는 것이다. 


Layer 2의 CSMA/CD(유선) 와 CSMA/CA(무선) 차이
CSMA/CD(유선): 대표적인 것이 Ehternet 
CSMA/CA(무선):  대표적인 것이 WIFI 

Ethernet과 비교하면, 예전의 Dummy Hub를 사용할때의 구성과 현재 RS485 구성 얼추 비슷하게 보인다. 
RS485는 CSMA/CD를  Layer2 즉, RS485 Tranceiver에서 처리못하고, 이를 RS232를 걸쳐 SW로 처리 해야한다. 

  • Ethernet의 CD(Collision Detection)
Ethernet의 경우, 예전에 Dummy Hub에 Station을 연결하면 할 수록 속도는 떨어진다.
이유는 Station들간에  충돌나면, Volatage 변경되어 CD(Collision Detection)발생하고, 이 때문에, 
JAM 신호를 보내어 모든 Device를 멈추게 한다. 
그리고, 이런일이 반복되어지면, Backoff Time이 더 늘어난다.  
결론적으로 Station을 추가하면 추가할수록 Bus 속도는 기하급수적으로 늦어진다. 

  • RS485의 CD(Collision Detection) 다른이유 
RS485와 Ethernet 의 비슷하지만, Voltage기반으로는 Collision Detection을 하기가 힘들다 .(이유는 간단하다.)
RS485 Tranceiver Differential Range가 넓어서, Bus에서 각 Device 의 RS485 Tranceiver 마다 구동되는 VCC가 다를수 있다.
예를들면, 같은 RS485 Bus 내에서도, 어떤 Device는 Voltage 3.3v 사용하고, 어떤 Device Voltage 5v를 사용가능하다.
이를 특정 Voltage Thrshold Level 정해서 Collision Detection으로 하기가 힘든 구조이다. 
(물론 HW 보완해서 하면 얼마든지 가능하다)

  • RS485 와 Volatage Range(-12v/+12V)
RS485의 Differential Voltage Range가 넓은 이유는 거리때문일 것이라고 생각한다.
간단히 생각해보면, 거리가 길어질 수록 선로는 길어지며, 선로 저항값이 점점 늘어난다. 


2. ESP32 Modbus 구성 

  • Modbus 협회 사이트 
Modbus Main 협회사이트인 것 같은데, 주요 볼 것은 검색해서 나오는 링크일 것 같으며, Member로는 등록을 안했다. 
 
  • ESP32의 Modbus 관련정보 
ESP32의 Modbus 지원내용과 API 관련내용을 자세히 설명 


2.1  Modbus의 기본구성 

Modbus를 검색만 해도 너무 많은 자료가 나오는데, 검색된 블로그 들어가서 글을 읽으면, 
아래내용과 틀린부분이 있어, 아래의 2개의 링크를 추천 

  • Modbus의 Protocol 관련 링크 
Modbus의 Protocol 설명과 실제 예제 및 분석과 비교 까지 해주어서 괜찮음 

Modbus 기본적인 설명이 잘 정리되어있으나, 상위와 용어가 조금씩 다름 주의 


  • Modbus의 Object(Data) Types
Modbus는 아래의 4가지 Type으로 Data접근가능.
2byte 전송형태와 1bit 전송형태 구분
이는 Read Only 와 Read Write 로 나뉨
Object typeAccessSizeAddress Space
CoilRead-write1 bit0x0000 – 0xFFFF
Discrete inputRead-only1 bit0x0000 – 0xFFFF
Input registerRead-only16 bits0x0000 – 0xFFFF
Holding registerRead-write16 bits0x0000 – 0xFFFF

Modbus를 하기위해서는 상위 각 Object Type Address Map을 정의해야하며, 이기반으로 통신 
Modbus RTU/ASCII 이건 둘 다 Function Code 기본으로 상위 Read/Write  넣음 

Input register/ Holding Register Map 구현예제 


  • Modbus Frame Format
Modbus Frame은 아래 ADU(Application Data Unit)로 구성되며, 다시 PDU(Protocol Data Unit)구성형태이다.
  1. ADU = Address + PDU + Error check
  2. PDU = Function code + Data

2.2 Modbus Frame 종류

Modbus Frame 종류와 각 Physical Interface 관계는 아래와 같으며, 이를 기반으로 연결되어 통신을 한다.
주로 가장 많이 사용되어지는 것이 Modbus RTU 와 Modbus TCP 인 것으로 보이며, 
이 두 개를 연결 Mapping하여 Serial <-> Ethernet에서도 Control 할 수 있는 구조로 확장해서 사용하기도 한다. 

  • Modbus Frame 종류
  1. Modbus RTU : Serial (RS232/RS485) Binary 목적으로 개발 
  2. Modbus ASCII : Serial (RS232/RS485) ASCII 을 이용  
  3. Modbus TCP:  TCP Protocol이 지원되며, 이는 Modbus RTU와 연결도 가능 

상위 내용을 보면, Modbus ASCII 보다는 RTU가 더 효율적으로 보이며, Modbus TCP 호환으로 보아도 
Modbus RTU를 사용해야 할 것 같다. 
** 상위 링크에서 Modbus RTU 와 ASCII를 비교가능 



Modbus RTU와 구조는 유사하지만, Binary용 전송이아니라, 세부사항들이 조금씩 다르며, DATA 기본단위도 2byte단위
  1. Start는 0x3A End는 0x0D,0x0A 로 감지목적사용 
  2. Address는 Station Address로 2byte 사용하여 총 247까지 사용가능
  3. Function는  2byte Object Type 방식을 선택되어지는 것으로 각 Function에 따라 동작 
  4. 상위 Object Type 방식에 Address Space는 DATA에 포함되며 세부사항은 Format 참조

주의: Modbus RTU 와 동일한 Data를 전송할 경우, 항상 2배의 Data 량을 소비

Unit Address (PLC Address): 2byte
  1. Address: 0  Broadcast 목적
  2. Address: 1 ~247, 각 Station의 Address로 이며 Unit Address 이라고도 함  (1byte)
Modbus PDUMax 253x2 = 506bytes
  1. Function:  아래 Wiki 참조 
  2. Data: Function에서 결정되며, 2byte 단위로 Encode 되어진다고 한다.
LRC: 2byte Checksum 이며, CRC가 아님 

NameLength (bytes)Function
Start1Starts with colon : (ASCII hex value is 3A)
Address2Station address
Function2Indicates the function codes like read coils / inputs
Datan × 2Data + length will be filled depending on the message type
LRC2Checksum (Longitudinal redundancy check)
End2Carriage return – line feed (CR/LF) pair (ASCII values of 0D0A


  1. Start/End는 0으로 RTU Packet의 감지목적으로 사용 
  2. Address는 Modbus RTU Slave의 Station Address로 1byte허용하여 총 247까지 사용가능
  3. Function는 상위 Object Type 방식을 선택되어지는 것으로 각 Function에 따라 동작 
  4. 상위 Object Type 방식에 Address Space는 DATA에 포함되며 세부사항은 Format 참조

Unit Address (PLC Address): 1byte
  1. Address: 0  Broadcast 목적
  2. Address: 1 ~247, 각 Station의 Address로 이며 Unit Address 이라고도 함  (1byte)
Modbus PDUMax 253 bytes
  1. Function:  아래 Wiki 참조 
  2. Data: Function에서 결정
CRC: 2bytes

총 256 Bytes

NameLength (bits)Function
Start28[citation needed]At least 3½ character times of silence (mark condition)
Address8Station address
Function8Indicates the function code; e.g., read coils/holding registers
Datan × 8Data + length will be filled depending on the message type
CRC16Cyclic redundancy check
End28[citation needed]At least 3½ character times of silence between frames

** 주의 : 상위 Length는 Bits 단위


상위 Frame과 호환성을 가지기 위해서 Function code와 Unit Identifier로 구성되며, 이는 Modbus RTU와 쉽게 호환 
  1. Transaction ID는 상위 Start/End 목적처럼 Sync를 위한 감지목적 
  2. Protocol ID는 Modbus TCP는 0 
  3. Unit ID는 기존의 Stations Adddress
  4. Function는 상위 Object Type 방식을 선택되어지는 것으로 각 Function에 따라 동작 
  5. 상위 Object Type 방식에 Address Space는 DATA에 포함되며 세부사항은 Format 참조
  6. Modbus RTU over TCP의 경우, 아래의 CRC가 추가. 

Unit Identifier (PLC Address): 1byte
  1. Address: 0  Broadcast 목적
  2. Address: 1 ~247, 각 Station의 Address로 이며 Unit Address 이라고도 함  (1byte)
Modbus PDUMax 253 bytes
  1. Function:  아래 Wiki 참조 
  2. Data: Function에서 결정
CRC:  Modbus RTU over TCP에서 사용 

NameLength (bytes)Function
Transaction identifier2For synchronization between messages of server and client
Protocol identifier20 for Modbus/TCP
Length field2Number of remaining bytes in this frame
Unit identifier1Server address (255 if not used)
Function code1Function codes as in other variants
Data bytesnData as response or commands

* Modbus RTU over TCP의 경우 상위 끝에 CRC추가되어 Modbus RTU와 호환가능 

  • 상위 모든 정보는 Wiki기반으로 가져온것이며 세부내용은 아래 참조 
세부내용은 특히 Function 및 동작순서는 아래 Wiki에 너무 자세히 잘 나옴 


2.3 Modbus Request /Response  분석 

아래는 동일한 Station Address / Function / Data 이용하여 각 Frame 마다 전송비교 분석이며, 이를 쉽게 이해 할 수 있다. 

  • Read Input Register 예제 분석 
1. Request: Address 200 의 Size 2개의 Data 요청
2. Response : Size:4 byte 와 Data 2개 (0x2710, 0xC350) 

-------------------------------------------------------------------------------------------------------------------------------------
                Request	                                              |  Response
-------------------------------------------------------------------------------------------------------------------------------------                 
Modbus ASCII    3A                                                    | 3A                                                             // MODBUS ASCII START
                30 31                                                 | 30 31                         (ASCII:01)                      // Station Address 0 1 
                30 34                                                 | 30 34                         (ASCII:04)                      // Function  0 4  
                30 30 43 38 30 30 30 32 (ASCII:00C80002)              | 30 34 32 37 31 30 43 33 35 30 (ASCII:04 27 10 C3 50 )       // Data x 2byte 
                33 31                                                 | 41 44                                                          // LRC (Checksum)
                0D 0A	                                              | 0D 0A                                                          // END (CR/LF)
--------------------------------------------------------------------------------------------------------------------------------------
Modbus RTU	01 	                                              | 01                                                             // MODBUS RTU: Station Address(Unit) 1 
                04                  	                              | 04                                                             // Function 4 Read Input Register 
        	00 C8 00 02        Address: 200  Size:2x2             | 04 27 10 C3 50           (Byte:4, Data:2 words)                // Data x 1byte  (Size, Data 2 Words)
        	F0 35	                                              | A0 39                                                          // CRC
--------------------------------------------------------------------------------------------------------------------------------------
Modbus TCP	00 14                                                 | 00 14                                                          // MODBUS TCP: Transaction ID 2byte
          	00 00                                                 | 00 00                                                          // Protocol ID 2byte 
          	00 06                                                 | 00 07                                                          // Length  2byte 
                01                                                    | 01                                                             // Station Address(Unit) 1
                04          	                                      | 04                                                             // Function 4
                00 C8 00 02	   Address: 200  Size:2x2             | 04 27 10 C3 50                                                 // Data x 1 byte
---------------------------------------------------------------------------------------------------------------------------------------

상위예제는 Station(Unit) 1 주소로 Function 4의 Request/Resonse 구조로 보내는 Data는 동일하다. 
간단히 보면, ASCII로 보낼 경우, Packet Size가 두 배가 되어진다. 
Protocol을 보면, RTU 와 TCP는 쉽게 호환되어지는 구조이다. 


  • Modbus-RTU Write 와 Exception 확인
상위와 동일하게 아래를 참조하여 분석하면 된다.
Slave에서 에러가 발생하면, Response에서 Function Code 90h 과 함께 Exception Code가 별도로 존재 


3. Modbus TEST 방법 

다양한 Tool이 존재하지만 유료도 존재하여 나의 경우 Python 기반으로 테스트를 진행하였다.
  • libmodbus 와 PyModbus 이용


3.1 Python 기반의 Pymodbus TEST 방법 및 분석  

Python Pymodbus Manual
현재 나도 이기반으로 TEST Program들을 작성했으며, 다양한 예제가 있어 편하다.

  • PC에서 ModbusTCP/RTU/ASCII Slave 테스트
PC가 Modbus Master가 되어 IOT (Modbus Slave) 테스트 할 경우 소스이며 각 Function에 관련된 함수에 따라 각각의 테스트 가능 
  1. 상위 read/write coill 및 read only discreate input 테스트 가능 (1bit)
  2. 상위 read/write holding register 및 read only input register 테스트 가능 (2byte)

보통의 경우, sync용 예제를 봐야하며, async의 경우는 빠르게 보내는 경우에 사용되어진다고 보면될꺼 같다.
내 생각으로는 sync 와 async의 차이는 아래라고 하는데 추후 다시 확인해야하겠지만, blocking 과  non-blocking 의 차이라고 생각되어짐 
기본적으로 Unix System programing or network programing 알면 대충 이해가 되실듯함 
  1. sync : request 와 responce를  각 확인 인 되어 동작 (blocking mode) 일 것 같음 
  2. async: request하고 response는  그냥 blocking 없이 동작 (non blocking mode) 일 것 같음 

# 간단하게 이런식으로 연결후 원하는 정보를 읽어서 확인하면 된다 
# pip install pymodbus 

from pymodbus.client.sync import ModbusSerialClient

def run_sync_client():
    
    client = ModbusSerialClient(
        method='rtu',
        port=Port,
        baudrate=Baud,
        timeout=3,
        parity='N',
        stopbits=1,
        bytesize=8
    )
    if client.connect(): 
        print('connected to Modbus Slave UNIT=',UNIT)
    else:
        print('failed to connect to the Modbus Slave UNIT=',UNIT)
    


  • PC의 PayloadBuilder 와 PayloadDecoder 사용 및 확인이유  
Holding Register와 Input Register는 기본적으로 2byte 통신이므로 Byte Order과 Word Order가 따라 결과 값이 달라질 수 있다. 
  1. Byte Order: BigEndian/ Little Endian : CPU에 종속됨 (Word의 저장되는 순서)
  2. Word Order: Big Endian / Little Endian:  소스에서 확인해봐야함
PayloadBuild의 경우 Slave도 Write를 할 경우가 발생하는데, 이때 Holding Register를 이용하여 Write할 때 필요 
PayloadDecoder의 경우 이를 다시 Decode를 할 경우 사용 

PayloadBuilder 예제 (주로 write시 사용)
2byte단위 통신이므로 다양한 포맷으로 변경 및 추가하여 이를 Packet 생성 

PayloadDecoder 예제 (주로 read시 사용)
2byte 단위 통신이므로 다른 포맷으로 변환시 사용 


ESP32의 내부 Ordering 확인사항 
사실 Word Order는 u16을 u32로 확장할 경우, 문제가 발생하는 것으로 보인다. (기본 32bit CPU의 word는 4byte)
예를들면 struct로 u16로 두개로 잡은 후에 이를 u16 pointer에서 u32로 변경할 경우 u16의 두개가 위치, 즉 address를  확인해야함
modbus 내부 api에서 변경할 가능성도 있으므로 이는 상위 wiki 세부확인

ESP32의 경우 내부적으로 분석해보면, ESP32의 경우 Bigendian 혹 정확히 말하며 Binary로 보내는게 맞다.   
이는 아래와 같이 Data 영역에 Byte Align으로 Data를 저장하여 이를 Memory 접근한다.

Byte Align 방법 
아래의 preprocessor로 1 byte align을 하여 byte ordering에 상관없이 사용하고 있다. 
#pragma pack(push, 1)
typedef struct
{
...
}...
#pragma pack(pop)

  • PC의 PayloadDecoder 결론사항 
PyModbus에서 입장에서 Byte OderBigEndian Word OrderLittel Endian으로 해야 Float32가 제대로 보인다 
개인생각으로는 PyModbus가 받은 Data를 함수내에 Stack영역에 저장하여 발생되어지는 문제로 생각되어진다.
u16->f32을 확장할 경우 PC(Program Counter) 의 Address 가 위로 증가 할지, 아래로 증가할지는 각 영역(Stack/BSS/Init)에 따라 달라진다 

현재 이 부분은 현재 PyModbus가 Data 저장을 Stack영역에 저장하여 상위와 같은 현상이 발생되어지는 것으로 생각되어진다. 


TEST Program 확인사항
상위를 먼저확인 후 Builder를 이용하여 u32으로 register를 write 한 후 decoder로 이를 재확인필요


ESP32의 Core Cadence의 Tensilica (Xtensa LX6) 


Xtensa 의 ABI (너무 간단하게만 나오며, 함수와 ELF 관련내용)
혹시 몰라 Xtensa의 ABI도 링크함, 
ARM이나, Power PC에 비해 너무 간단하게 나오며, 아마 다른문서가 있을 거라고 생각됨
일단, 간단하게만 알고나 있도록하자.  

10/21/2022

ESP32 Secure Boot 와 OTP

1. ESP32 Secure Boot 기본개념

  • ARM/ESP32 Secure Boot 관련비교 내용 
ARM의 Secure Boot 와 ESP32의 Secure Boot의 기본개념을 설명 
반드시 이해를 해야하며, 이를 이해해야 아래의 내용도 이해가능 

  • ESP32의 Startup Boot Flow
  1. 1st stage bootloader : ESP32 내부에 존재하는 Bootloader로 ROM에 이미 저장
  2. 2nd stage bootloader: ESP32의 SPI Flash 저장되어진 실제적인 bootloader image 이며 Partition table 접근가능
  3. application image: app image로 factory or ota 영역에 저장되는 application image

상위와 같이 ESP32는 Booting하며, User가 신경써야 할 부분은 2nd StageApplication image이다. 


1.1 ESP32 Secure Boot Version  

Espressif에서는 Secure Boot V1, V2을 제공하고 있지만, V2 더 권장하고 있으며, 다음과 같은 차이가 존재한다. 

  1. SecureBoot v1 : ESP32 Silicon Version1/2에서 지원가능
  2. SecureBoot v2 : ESP32 Silicon Version3에서 지원가능

  • ESP32 Secure Boot v1 for ESP32 Revision1/2
  1. AES-256-ECB 방식으로 bootloader Encoding/Decoding  
  2. ECSDA Signature 기반으로 appImage Signing/Verification  Image검증  
  3. OTP(eFUSE) 의 Block 2에  AES 256 bit 저장이용 
  4. OTP(eFUSE) 의 ABS_DONE_0 설정 

  • ESP32 Secure Boot v2 for ESP32 Revision3
  1. RSA3072 Key 기반으로 Signature 기반 bootloader Signing/Verification  Image검증  
  2. RSA3072 Key 기반으로 Signature 기반 appImage Signing/Verification  Image검증     
  3. OTP(eFUSE) 의 Block 2에 SHA-256 bit digest 이용하여 RSA3072 Public Key 검증 
  4. OTP(eFUSE) 의 ABS_DONE_1 설정 


1.2  Secureboot 의 Signing 과 Verfication 이해 

Secure Boot의 기본개념인 Signing 과 Verfication을 알아보도록 하자 
흔히, 비대칭키를 이용하여 Sign을 하여, Signature 생성하고, 이를 검증(Verfication)하는 것이다.

Android System의 Sign 과 Vefication 이해 

암호화 기본개념 

  • Secure Boot Signing
  1. 비대칭키 기반 사용 (RSA,ECDSA)
  2. Build를 한 후 image를 생성할때 Private Key로 Signature를 생성

  • Secure Boot Verification 
  1. 비대칭키 기반 사용 (RSA,ECDSA)
  2. 이미 생성된 Signature를 Public Key를 이용하여 이를 검증 

2. ESP32 Secure Boot 설정 과 진행 


우선 Signature를 만들기 위해서, 즉 Signing을 위한 Key가 필요하다. 


2.1 RSA Signing Key 생성방법 


Window용 ESP-IDF가 버그가 있어 아래와 같이 export.bat 수정하도록 한다.  
아래는 Powershell 용이 아니라 Command 용이므로 참고하도록하자. 

  • ESP-IDF 버그 수정 (v.4.2.3)
수정후 espsecure.py 사용가능
cd  %userprofile%\esp\esp-idf\export.bat  //수정 export.bat (Window에서 espsecure.py 미지원)

....
DOSKEY idf.py=python.exe "%IDF_PATH%\tools\idf.py" $*
DOSKEY esptool.py=python.exe "%IDF_PATH%\components\esptool_py\esptool\esptool.py" $*
DOSKEY espefuse.py=python.exe "%IDF_PATH%\components\esptool_py\esptool\espefuse.py" $*
DOSKEY espsecure.py=python.exe "%IDF_PATH%\components\esptool_py\esptool\espsecure.py" $*
DOSKEY otatool.py=python.exe "%IDF_PATH%\components\app_update\otatool.py" $*
DOSKEY parttool.py=python.exe "%IDF_PATH%\components\partition_table\parttool.py" $*

echo Checking if Python packages are up to date...
python.exe "%IDF_PATH%\tools\check_python_dependencies.py"
if %errorlevel% neq 0 goto :end

echo.
echo Done! You can now compile ESP-IDF projects.
echo Go to the project directory and run:
echo.
echo   idf.py build
echo.
....


RSA Signing Key를 꼭 espsecure.py로 생성할 필요는 없으며, openssl로 쉽게 생성가능.

  • RSA3072 Key 생성방법-A 
openssl을 이용하여  Singing Key 생성 방법 
$ openssl genrsa -out my_secure_boot_signing_key.pem 3072
$ cp ./my_secure_boot_signing_key.pem /mnt/d/Works/   

  • RSA3072 Key 생성방법-B
espsecure.py 이용하여 쉽게 생성가능하며, 각 Version 별로 설정가능  
$ espsecure.py generate_signing_key -v2 test1.pem   
$ espsecure.py generate_signing_key -v1 test1.pem   
$ espsecure.py sign_data --version 2 --keyfile my_secure_boot_signing_key.pem  --output ./build/app_sign.bin ./build/app.bin 

  • ESP SecureBoot Version1
ESP32의 경우, Revision3 부터 사용권장
PEM 방식으로 ECDSA Signing/Verification 동작 

ESP32 SecureBootV1 Manual 

  • ESP SecureBoot Version2 
이를 좀 더 권장하며, ESP32 ECO3 이상에서 사용가능하며, RSA-PSS 방식이라고 말한다.
PEM 의 RSA3072 기반으로 Signing/Verfication 방식
OTP(eFUSE, One Time Programming)  Block2 의 Secure Boot에 RSA Public Key의 Hash를 넣는 구조

ESP32의 Secure Boot 기본이해 

  • ESP32 와 외부 Security Chip 사용 
ESP32에서 ECDSA의 Sign/Verify를 사용하기 위해서 외부 Chip를 사용 
이전에 설명한 CMVP/KCMVP 기능이라고 생각하면 될 것 같으며, 확장기능으로 이를 이용하여 TLS도 가능하다 


2.2 OTP(eFuse) 구조 와 역할 

ESP32 의 경우, 1024bit 의 OTP(One Time Programmable, eFUSE)를 가지고 있고, 각 Block은 256bit로 총 4개로 나누어 분리하여 사용하도록 한다. 
주의 해야할 것은 OTP(One Time Programmable) 한 번만 Write가 가능하므로, 신중하게 하도록하자. 
실패하면, 쓸모가 없어진다. 

  • OTP(eFUSE) 구조 

  • OTP(One Time Programable,eFuse) Manual 
ESP-IDF Version에 따라 조금씩 다르므로 각 버전에 맞게 참조하도록 하자 

  • ESP32 burn-efuse 설명 


  • ESP32 OTP 사용방법 
설정하는 방법은 현재 Command 중심으로 하는게 제일 편하며, ESP-IDF 개발환경에서 VS Code용 EFUSE Exploerer를 별도로 제공을 해주고 있다. 
  1. UI 중심: VS Code EFUSE Exploerer
  2. Command 중심 아래 참조 


2.3 OTP(eFuse) Command 기반 사용법 

실제로 ESP32에서 제공하는 eFuse, OTP 설정값들을 확인해보고, 제어를 해보도록하자. 

  • Window에서 ESP-IDF 활성화   
ESP-IDF가 현재 venv기반의 Python으로 되어있으므로 이를 활성화 부터 해야한다.

Command로 할 경우, 
%userprofile%\.espressif\python_env\idf4.2_py3.8_env\Scripts\activate.bat
%userprofile%\esp\esp-idf\export.bat

PowerShell 할 경우,
$Env:userprofile\.espressif\python_env\idf4.2_py3.8_env\Scripts\activate.ps1
$Env:userprofile\esp\esp-idf\export.ps1

Window 용 PowerShell 관련내용 
주의, Powershell의 경우 Version 따라 동작이 많이 다르며, 점차 Linux 처럼 변경 중 


  • idf.py show_efuse_table (각 OTP 내부구성파악)
OTP인 eFuse에 각 Table 구성과 Field name을 쉽게 파악가능하다. 

(idf4.2_py3.8_env) D:\Works\Project\>idf.py show_efuse_table  //ESP Project 내에서 실행하며, 각 Field name 파악
Executing action: show_efuse_table
Running ninja in directory d:\works\project\mod610_sw\build
Executing "ninja show_efuse_table"...
[1/1] cmd.exe /C "cd /D D:\Works\Project\mod610_sw\build\esp-idf\efus...ts/efuse/esp32/esp_efuse_table.csv -t esp32 --max_blk_len 192 --info" 
Parsing efuse CSV input file C:/Users/jhlee/esp/esp-idf/components/efuse/esp32/esp_efuse_table.csv ...
Verifying efuse table...
Max number of bits in BLK 192
Sorted efuse table:
#       field_name                      efuse_block     bit_start       bit_count  //Field name 과 Block 위치 와 Bit Start 파악
1       WR_DIS_FLASH_CRYPT_CNT          EFUSE_BLK0         2               1
2       WR_DIS_BLK1                     EFUSE_BLK0         7               1
3       WR_DIS_BLK2                     EFUSE_BLK0         8               1
4       WR_DIS_BLK3                     EFUSE_BLK0         9               1
5       RD_DIS_BLK1                     EFUSE_BLK0         16              1
6       RD_DIS_BLK2                     EFUSE_BLK0         17              1
7       RD_DIS_BLK3                     EFUSE_BLK0         18              1
8       FLASH_CRYPT_CNT                 EFUSE_BLK0         20              7
9       UART_DOWNLOAD_DIS               EFUSE_BLK0         27              1
10      MAC_FACTORY                     EFUSE_BLK0         32              8
11      MAC_FACTORY                     EFUSE_BLK0         40              8
12      MAC_FACTORY                     EFUSE_BLK0         48              8
13      MAC_FACTORY                     EFUSE_BLK0         56              8
14      MAC_FACTORY                     EFUSE_BLK0         64              8
15      MAC_FACTORY                     EFUSE_BLK0         72              8
16      MAC_FACTORY_CRC                 EFUSE_BLK0         80              8
17      CHIP_VER_DIS_APP_CPU            EFUSE_BLK0         96              1
18      CHIP_VER_DIS_BT                 EFUSE_BLK0         97              1
19      CHIP_VER_PKG                    EFUSE_BLK0        105              3
20      CHIP_CPU_FREQ_LOW               EFUSE_BLK0        108              1
21      CHIP_CPU_FREQ_RATED             EFUSE_BLK0        109              1
22      CHIP_VER_REV1                   EFUSE_BLK0        111              1
23      ADC_VREF_AND_SDIO_DREF          EFUSE_BLK0        136              6
24      XPD_SDIO_REG                    EFUSE_BLK0        142              1
25      SDIO_TIEH                       EFUSE_BLK0        143              1
26      SDIO_FORCE                      EFUSE_BLK0        144              1
27      CHIP_VER_REV2                   EFUSE_BLK0        180              1
28      ENCRYPT_CONFIG                  EFUSE_BLK0        188              4
29      CONSOLE_DEBUG_DISABLE           EFUSE_BLK0        194              1
30      ABS_DONE_0                      EFUSE_BLK0        196              1
31      DISABLE_JTAG                    EFUSE_BLK0        198              1
32      DISABLE_DL_ENCRYPT              EFUSE_BLK0        199              1
33      DISABLE_DL_DECRYPT              EFUSE_BLK0        200              1
34      DISABLE_DL_CACHE                EFUSE_BLK0        201              1
35      ENCRYPT_FLASH_KEY               EFUSE_BLK1         0              192
36      SECURE_BOOT_KEY                 EFUSE_BLK2         0              192
37      MAC_CUSTOM_CRC                  EFUSE_BLK3         0               8
38      MAC_CUSTOM                      EFUSE_BLK3         8               48
39      ADC1_TP_LOW                     EFUSE_BLK3         96              7
40      ADC1_TP_HIGH                    EFUSE_BLK3        103              9
41      ADC2_TP_LOW                     EFUSE_BLK3        112              7
42      ADC2_TP_HIGH                    EFUSE_BLK3        119              9
43      SECURE_VERSION                  EFUSE_BLK3        128              32
44      MAC_CUSTOM_VER                  EFUSE_BLK3        184              8

Used bits in efuse table:
EFUSE_BLK0
[2 2] [7 9] [16 18] [20 27] [32 87] [96 97] [105 109] [111 111] [136 144] [180 180] [188 191] [194 194] [196 196] [198 201]

EFUSE_BLK1
[0 191]

EFUSE_BLK2
[0 191]

EFUSE_BLK3
[0 55] [96 159] [184 191]

Note: Not printed ranges are free for using. (bits in EFUSE_BLK0 are reserved for Espressif)



  • espefuse.py 사용법 (OTP 제어방법 확인)
아래와 같이 기본사용법을 확인을 반드시 하도록하며, 각 명령어에 따라 사용하도록하자. 
(idf4.2_py3.8_env) D:\Works\Project> espefuse.py  //esefuse 사용법확인 
espefuse.py v3.1-dev
usage: espefuse.py [-h] [--chip {auto,esp32,esp32s2,esp32s3beta2,esp32s3beta3,esp32c3}] [--baud BAUD] [--port PORT]
                   [--before {default_reset,no_reset,esp32r1,no_reset_no_sync}] [--debug] [--virt] [--path-efuse-file PATH_EFUSE_FILE]        
                   [--do-not-confirm]
                   {burn_efuse,read_protect_efuse,write_protect_efuse,burn_block_data,burn_bit,adc_info,dump,summary,burn_key,burn_key_digest,set_flash_voltage,burn_custom_mac,get_custom_mac}
                   ...

positional arguments:
  {burn_efuse,read_protect_efuse,write_protect_efuse,burn_block_data,burn_bit,adc_info,dump,summary,burn_key,burn_key_digest,set_flash_voltage,burn_custom_mac,get_custom_mac}
                        Run espefuse.py {command} -h for additional help
    burn_efuse          Burn the efuse with the specified name   //Feild name 기반 Write 기능,  burn_bit 와 동일 
    read_protect_efuse  Disable readback for the efuse with the specified name
    write_protect_efuse
                        Disable writing to the efuse with the specified name
    burn_block_data     Burn non-key data to EFUSE blocks. (Don't use this command to burn key data for Flash Encryption or ESP32 Secure      
                        Boot V1, as the byte order of keys is swapped (use burn_key)).
    burn_bit            Burn bit in the efuse block.   //burn_efuse 거의 동일하며, Block 과 Bit를 입력하여 Write  
    adc_info            Display information about ADC calibration data stored in efuse.
    dump                Dump raw hex values of all efuses
    summary             Print human-readable summary of efuse values
    burn_key            Burn a 256-bit key to EFUSE: BLOCK1, flash_encryption, BLOCK2, secure_boot_v1, secure_boot_v2, BLOCK3
    burn_key_digest     Parse a RSA public key and burn the digest to eFuse for use with Secure Boot V2
    set_flash_voltage   Permanently set the internal flash voltage regulator to either 1.8V, 3.3V or OFF. This means GPIO12 can be high or    
                        low at reset without changing the flash voltage.
    burn_custom_mac     Burn a 48-bit Custom MAC Address to EFUSE BLOCK3.
    get_custom_mac      Prints the Custom MAC Address.

optional arguments:
  -h, --help            show this help message and exit
  --chip {auto,esp32,esp32s2,esp32s3beta2,esp32s3beta3,esp32c3}, -c {auto,esp32,esp32s2,esp32s3beta2,esp32s3beta3,esp32c3}
                        Target chip type
  --baud BAUD, -b BAUD  Serial port baud rate used when flashing/reading
  --port PORT, -p PORT  Serial port device
  --before {default_reset,no_reset,esp32r1,no_reset_no_sync}
                        What to do before connecting to the chip
  --debug, -d           Show debugging information (loglevel=DEBUG)
  --virt                For host tests, the tool will work in the virtual mode (without connecting to a chip).
  --path-efuse-file PATH_EFUSE_FILE
                        For host tests, saves efuse memory to file.
  --do-not-confirm      Do not pause for confirmation before permanently writing efuses. Use with caution.



  • espefuse.py 사용방법 (OTP 설정값 확인용)
실제 Serial에 연결하여 각 설정 값을 확인하도록 하자 
(idf4.2_py3.8_env) D:\Works\Project> espefuse.py -p COM15 summary     //OTP 설정된것 확인용 
Connecting....
Detecting chip type... ESP32
espefuse.py v3.1-dev
EFUSE_NAME (Block)                       Description  = [Meaningful Value] [Readable/Writeable] (Hex Value)
----------------------------------------------------------------------------------------
Calibration fuses:
BLK3_PART_RESERVE (BLOCK0):              BLOCK3 partially served for ADC calibration data   = False R/W (0b0)
ADC_VREF (BLOCK0):                       Voltage reference calibration                      = 1114 R/W (0b00010)

Config fuses:
XPD_SDIO_FORCE (BLOCK0):                 Ignore MTDI pin (GPIO12) for VDD_SDIO on reset     = False R/W (0b0)
XPD_SDIO_REG (BLOCK0):                   If XPD_SDIO_FORCE, enable VDD_SDIO reg on reset    = False R/W (0b0)
XPD_SDIO_TIEH (BLOCK0):                  If XPD_SDIO_FORCE & XPD_SDIO_REG                   = 1.8V R/W (0b0)
CLK8M_FREQ (BLOCK0):                     8MHz clock freq override                           = 55 R/W (0x37)
SPI_PAD_CONFIG_CLK (BLOCK0):             Override SD_CLK pad (GPIO6/SPICLK)                 = 0 R/W (0b00000)
SPI_PAD_CONFIG_Q (BLOCK0):               Override SD_DATA_0 pad (GPIO7/SPIQ)                = 0 R/W (0b00000)
SPI_PAD_CONFIG_D (BLOCK0):               Override SD_DATA_1 pad (GPIO8/SPID)                = 0 R/W (0b00000)
SPI_PAD_CONFIG_HD (BLOCK0):              Override SD_DATA_2 pad (GPIO9/SPIHD)               = 0 R/W (0b00000)
SPI_PAD_CONFIG_CS0 (BLOCK0):             Override SD_CMD pad (GPIO11/SPICS0)                = 0 R/W (0b00000)
DISABLE_SDIO_HOST (BLOCK0):              Disable SDIO host                                  = False R/W (0b0)

Efuse fuses:
WR_DIS (BLOCK0):                         Efuse write disable mask                           = 0 R/W (0x0000)
RD_DIS (BLOCK0):                         Efuse read disable mask                            = 0 R/W (0x0)
CODING_SCHEME (BLOCK0):                  Efuse variable block length scheme
   = NONE (BLK1-3 len=256 bits) R/W (0b00)
KEY_STATUS (BLOCK0):                     Usage of efuse block 3 (reserved)                  = False R/W (0b0)

Identity fuses:
MAC (BLOCK0):                            Factory MAC Address
   = c4:dd:57:71:08:08 (CRC 0x5f OK) R/W
MAC_CRC (BLOCK0):                        CRC8 for factory MAC address                       = 95 R/W (0x5f)
CHIP_VER_REV1 (BLOCK0):                  Silicon Revision 1                                 = True R/W (0b1)
CHIP_VER_REV2 (BLOCK0):                  Silicon Revision 2                                 = True R/W (0b1)
CHIP_VERSION (BLOCK0):                   Reserved for future chip versions                  = 2 R/W (0b10)
CHIP_PACKAGE (BLOCK0):                   Chip package identifier                            = 1 R/W (0b001)
MAC_VERSION (BLOCK3):                    Version of the MAC field                           = 0 R/W (0x00)

Security fuses:
FLASH_CRYPT_CNT (BLOCK0):                Flash encryption mode counter                      = 0 R/W (0b0000000)
UART_DOWNLOAD_DIS (BLOCK0):              Disable UART download mode (ESP32 rev3 only)       = False R/W (0b0)
FLASH_CRYPT_CONFIG (BLOCK0):             Flash encryption config (key tweak bits)           = 0 R/W (0x0)
CONSOLE_DEBUG_DISABLE (BLOCK0):          Disable ROM BASIC interpreter fallback             = True R/W (0b1)
ABS_DONE_0 (BLOCK0):                     Secure boot V1 is enabled for bootloader image     = False R/W (0b0)
ABS_DONE_1 (BLOCK0):                     Secure boot V2 is enabled for bootloader image     = False R/W (0b0)
JTAG_DISABLE (BLOCK0):                   Disable JTAG                                       = False R/W (0b0)
DISABLE_DL_ENCRYPT (BLOCK0):             Disable flash encryption in UART bootloader        = False R/W (0b0)
DISABLE_DL_DECRYPT (BLOCK0):             Disable flash decryption in UART bootloader        = False R/W (0b0)
DISABLE_DL_CACHE (BLOCK0):               Disable flash cache in UART bootloader             = False R/W (0b0)
BLOCK1 (BLOCK1):                         Flash encryption key
   = 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 R/W
BLOCK2 (BLOCK2):                         Secure boot key
   = 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 R/W
BLOCK3 (BLOCK3):                         Variable Block 3
   = 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 R/W

Flash voltage (VDD_SDIO) determined by GPIO12 on reset (High for 1.8V, Low/NC for 3.3V).
상위보다는 거의 사용하지 않겠지만, 다른 기계와 설정값 비교할때는 dump가 최고 
(idf4.2_py3.8_env) D:\Works\Project> espefuse.py -p COM15 dump //OTP 설정된것 확인용 
Connecting....
Detecting chip type... ESP32
BLOCK0          (                ) [0 ] read_regs: 00000000 57710808 005fc4dd 0000a200 00000237 00100000 00000004
BLOCK1          (flash_encryption) [1 ] read_regs: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
BLOCK2          (secure_boot_v1 s) [2 ] read_regs: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
BLOCK3          (                ) [3 ] read_regs: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
espefuse.py v3.1-dev
만약, MAC을 사서 사용한다면 그때 설정하고 확인 
(idf4.2_py3.8_env) D:\Works\Project> espefuse.py -p COM15 get_custom_mac  //기본으로 사용하므로 없음
Connecting.....
Detecting chip type... ESP32
espefuse.py v3.1-dev
Custom MAC Address is not set in the device.

  • Secure Boot 설정완료 후 OTP 설정 할 것 
SecureBoot를 완벽히 진행하려면, 나중에는 JTAG 과 UART를 막아야 하며, 이후 부터 OTA로만 진행 
  1. ESP32 의 JTAG 막기 
  2. ESP32 UART BOOT 막기 
//EfuseDefineFields
(idf4.2_py3.8_env) D:\Works\Project> espefuse.py -p COM15 burn_efuse   JTAG_DISABLE 1        --before default_reset --do-not-confirm   
(idf4.2_py3.8_env) D:\Works\Project> espefuse.py -p COM15 burn_efuse   UART_DOWNLOAD_DIS 1   --before default_reset --do-not-confirm   //주의: 이후 Serial로 eFUSE도 동작안됨
(idf4.2_py3.8_env) D:\Works\Project> espefuse.py -p COM15 burn_efuse   CONSOLE_DEBUG_DISABLE 1

(idf4.2_py3.8_env) D:\Works\Project> espefuse.py -p COM15 burn_bit BLOCK0 198   //DISABLE_JTAG  , 상위와 동일 
(idf4.2_py3.8_env) D:\Works\Project> espefuse.py -p COM15 burn_bit BLOCK0 27    //UART_DOWNLOAD_DIS
(idf4.2_py3.8_env) D:\Works\Project> espefuse.py -p COM15 burn_bit BLOCK0 194   //CONSOLE_DEBUG_DISABLE

--before default_reset --do-not-confirm  반드시 사용 


(idf4.2_py3.8_env) D:\Works\Project> espefuse.py -p COM15 adc_info

(idf4.2_py3.8_env) D:\Works\Project> espefuse.py -p COM15 read_protect_efuse  BLOCK0
(idf4.2_py3.8_env) D:\Works\Project> espefuse.py -p COM15 write_protect_efuse



eFUSE API를 사용할 경우 



10/13/2022

Secure Boot (ESP32/ARM) 비교 및 기본이해

1. 기본 Certificate 활용 및 암호화 이해 

일반 기본 암호화 개념부터 알고, 그 다음에 Cetificate 를 왜 사용하는지 부터 알아보도록하자.
정확히 Authentication(인증)이라고 하는게 맞을 것 같다. 

주로 많이 사용되는 것은 TLS/SSL일 것이며, 이를 이용한 HTTPS 비롯하여 다양한 네트워크 프로토콜에서 사용되어지고 있다. 
그리고, 이를 넘어 Security Boot 를 비롯하여, Android app 인증등 많은 곳에서 사용되어지는 암호화 기술이다. 


  • 각 기본용어확인 
Authentication 과 Public Key 기반의 Certificate 및 Format X.509

보통 Certificate는 X.509 Format로 저장되어지므로 상위 기본내용은 알아두도록 하자. 

  • 암호화 기본개념
암호화의 기본개념과 Cetificate 기반의 TLS 기본이해 

  • Android의 인증시스템 구조 
Android에서 App을 인증하는 방법으로 Cerficate 기반의 Singining 과 Verfication  

  • SELinux(Security-Enhanced Linux)
SELinux로  Linux에서 인증된 App 과 그렇지 못한 App을 구분하여 실행하는 구조이다. (상위 Android 와 비슷하다.)  
OP-TEE기능으로 구현이 되어있을 지도 모르며, Selinux 기능은 Kernel Argument로 on/off를 할 수 있는 것으로 기억하며 Kernel config도 있다. 

  • ECDSA  Key 및 Certificate 생성 및 분석 
요즘 대세인 ECSDA Cerficiate 구조 구성 및 세부분석 과 사용방법 

  • WIFI-WPA 인증(Authentication)
WIFI의 EAP methods에서도 사용되어지며, 이부분은 Layer 2부터 같이 동작되어지며, WIFI기본 동작으로 알고 있어야 한다. 


1.1 PKCS11 와 HW Module  

내가 PKCS11를 처음 접한 것은 KCMVP를 다루면서 관련 인증서들을 분석하며 알게되면서 부터일 것 같다.
 
일단 PKCS11는 HSMs/TPM/KCMVP/CMVP/SmartCard/uSIM 등에서 주로 HW 와 함께 사용되는 기술이라고 생각되면 될 꺼 같다. 

쉬운 예를들면, 특정 HW모듈에 PKCS11 Interface기반으로 인증서를 넣어 못 건들게 하고 이 기반으로 TLS 방식으로 암호화를 하는 것이다. 
대표적으로 쉽게 이해가능한게 TPM일 것 같다. 요즘 대부분 컴퓨터에 설치되어있으니까. 

이외에도 CMVP라고 HW모듈기반으로 TLS/DTLS 통신하는 것이 있으며, 다양한 것이 있으므로 너무 암호화를 한정적으로만  생각할 수가 없을 것 같다. 
이 내용을 Secure Boot에 다루는 이유는 이 기반으로 Booting에도 사용되어지기 때문이며, 이외 다양하게 사용되어진다. 

  • PKCS(Public-Key Cryptography Standard) 11 관련내용
PKCS11의 경우 Token or Key를 저장하는 구조이며, 이에 관련 된 정보들은 아래 
  

2. ARM HW 암호화 구성   

ARM의 경우, TrunstZone과 다양한 HW 암호화를  제공하고 있어, ARM을 기반으로 보안기능을 제공하고 있으며, 
이 기능기반으로 Secure Boot로 뿐만 아니라 다양한 암호화 기능을 사용가능하다. 
암호화된 App 실행를 비롯하여, TLS/SSL 를 비롯하여 다양하게 사용되어진다.  

암호화에 대해서 너무 한정적으로 생각하지 말도록하자. 


2.1 ARM의 TrustZone 과 TEE 기능이해 

ARM의 경우는 TEE(Trust Execution Environment)REE(Rich Execution Environment)로 ARM 내부에서 분리되어 실행되어진다. 
이는 기본으로 MMU를 기반으로 Protection을 진행하여 제어하는 방법이 필수 일 것 같다. 

  1. TEE(Trust Execution Environment) : ARM의 TrustZone을 이용하여 실행되는 환경구조 
  2. REE(Rich Execution Environment): ARM의 TrustZone을 사용안하는 환경구조 


  • ARM의 TrustZone 과 MMU 사용  
다중 Core를 사용하다보니, MMU기반으로 분리를 하는게 쉽지는 않는 것으로 보인다. 


  • ARM에서 PKCS11 기반으로 OP-TEE 사용
NXP의 i.MX8의 Secure Boot 

  • OP-TEE를 이용하여 Secure Application 실행 
TA(Truested Application)

PKCS#11 과 OP-TEE의 i.MX8 관련내용(PCKS#11 TA)

PKCS#11 과 OP-TEE 의 IoT 관련내용 

OP-TEE 사용법과 이해  


  • ARM TrustZone
ARM에서 제공하는 HW적은 암호화 기능이며, 이를 확장하여 사용하는 것 TEE/REE    

  • Uboot의 TEE(Trusted Execution Environment) 
TEE(Trusted Execution Environment) 이며, 즉 Secure Boot를 비롯하여 Secure App, 다양한 암호화를 ARM HW부터 제공한다. 
Linux Command인 TEE 명령어와 혼동하지 말도록하자 

  • Uboot의 TEE(Trusted Execution Environment) 와 OP-TEE(Open Portable TEE) 예제 
Uboot에서 TEE를 설정하여 Secure Boot를 진행가능하며, OP-TEE도 설정가능 

  • Yocto에서는 OP-TEE(Open Portable TEE) 제공
이를 이용하여 Secure Boot를 비롯하여 인증된 App을 실행가능 한걸로 기억하며, 유사기술이 SELinux인걸로 기억한다. 

기존에 알고있는것을 다 정리 할 수 없을 것 같아 상위 정도로만, ARM 관련사항 정리를 마무리하고, 
TI Sitara or DM Series 확인을 한번 더 확인 

3. ESP32 HW 암호화

ESP32의 경우는 HW 암호화 기능과 OTP(One-Time Programmable), 즉 eFUSE를 제공하며, 다양한 암호화 기능을 제공한다.  
여기서 OTP(One-Time Programmable)이며, OTP(One-Time Password)이 가 아니므로 혼동하지 말자. 

결론적으로, ESP32도  eFUSE(One Time Programing) 이곳에서는 Secure Boot  PKCS11 기반의 Secure Boot과 거의 동일한 걸로 보이며, 
ARM처럼 HW 암호화기능 있어, 거의 유사하게 동작가능하며, 이외 ESP32 HW 암호화 기능도 제공해주고 있다. 

ESP32를 가격이 싸다고 만만히 볼만한 MPU는 아닌것 같으며, 정말로 다양한 기능을 제공하고 있다

3.1 ESP32의 Secure Boot 

ESP32의 Secure Boot의 OTP(One Time Programing,eFUSE)기반으로 진행되는 Siging/Verfication 기술이며, 이를 ESP32에서 제공을 해준다. 
ESP32의 경우도 역시 내부적으로 HW적으로 암호화 기능을 제공해지만, ARM의 TrustZone 동작방식과는 다르며,
내부적으로 간단하게 구성하여 제공해주고 있다. 




  • OTP(One Time Programing,eFUSE)
ESP32 내부에 제공해주는 1024bit 저장장소 제공해주고 있으며, 각 256bit 씩 4개의 Block으로 관리되어 지고 있다. 
OTP(eFUSE)의 경우 0 -> 1로만 변경가능하며, 한번 1로 변경되면 변경 불가능하다. 
https://blog.espressif.com/understanding-esp32s-security-features-14483e465724


  • ESP32 의 Secure Boot 의 전체 Flow  
Reset 후 ROM에서 1st stage 인 ROM에서 Boot를 하고 OTP(eFUSE)의 설정에 따라 동작이 되어진다. 
  1. OTP(eFUSE)에 저장된 Key 값 기반으로 Flash의 BootLoader를 Load 하기 전에 Verify하고 Booting  
  2. Flash의 Bootloader는 Bootloader 처럼 App image를 Verify를 진행하여 Booting 

https://blog.espressif.com/understanding-esp32s-security-features-14483e465724

상위구조를 쉽게 설명하면, OTP(One Time Programiming, eFUSE)에는 저장소의 한계 (256bit) 때문에 Hash(Token) 이용하여 한 번 더 검증하는 방식이다. 
보통 다른 곳에서는 이 Hash를 Token이라고도 많이 부르기도 한다. 


  • SecureBoot 의 BootLoader 구성 과 OTP(eFuse) 
  1. Bootloader Image:  Flash 에서 실행가능한 일반적인 image 이며, 일반부팅은 이것만 필요 
  2. RSA Signature:  Certificate의 Signature 처럼 상위 Image의 RSA3072기반으로 Private Key 로 생성 
  3. RSA 3072 Public Key: 상위 RSA Signature를 확인하기 위해서 Public Key가 필요 
  4. OTP(eFUSE): 저장된 Key의 Hash(SHA-256)로 RSA 3072 Public Key가 Verify 검증 


  • Bootloader 를 실행하기 전에 아래의 동작을 수행 
  1. OTP(eFuse)의 Hash를 이용하여 RSA 3072 Public Key가 맞는지 확인(Public Key Verfication)
  2. RSA 3072 Public Key를 이용하여 RSA Signature를 확인가능  
  3. Bootloader 실행 (RSA Signature는 이미 검증된 것임)

https://blog.espressif.com/understanding-esp32s-security-features-14483e465724


  • Flash 의 App Image의 기본구성 
상위와 동일한 구조 구조 이므로 생략 

  • Flash Encryption 
Secure Boot 기능을 넘어  Flash 저장된 정보를 ESP32 HW적으로 AES Key기반으로 Decrytion을 하여 동작가능하게 하는 기능이다. 
하지만, 이 기능을 사용하면, ESP32 성능과 Flash 기반의 XiP가 어떻게 될지가 좀 의문이다. 
그림상으로는 완벽하지만, 항상 실행해보면 문제가 생기는 경험을 많이 했기때문에, 나중에 한번 해보고 말을 해야 할 것 같다. 
(특히, SRAM 문제, Cache도 SRAM에 포함되며, 설정에 따라 SRAM 사용구조가 변경)

https://blog.espressif.com/understanding-esp32s-security-features-14483e465724




ESP32-S2 Secure Boot