Почта
Диск
     

Аналого-цифровое преобразование

Большинство AVR контроллеров (например, Atmel ATmega328P) имеют 8 аналоговых каналов измерения напряжения, подключаемых по очереди к 10-ти разрядному АЦП через мультиплексор. Диапазон измеряемого напряжения лежит между 0 В и напряжением AREF, которое как правило равно +5 В (для каналов с 1 по 7) и +1.1 В (для 8 канала). Верхняя граница измеряемого напряжения AREF может быть задана путем подачи необходимого напряжения на контакт AREF микроконтроллера и установки битов регистра ADMUX микроконтроллера.
Рассмотрим пример чтения уровня напряжения на контакте A0 в диапазоне от 0 до +5 В и вывода измеренных значений в последовательный порт. Интервал между измерениями составляет 0.5 секунды.
void setup() {
    Serial.begin(9600);
}

void loop() {
  int sensorValue = analogRead(A0);
  float voltage = sensorValue * (5.0 / 1023.0);
  Serial.println(voltage);
  delay(500);
}
Для отображения результатов измерений на экране монитора компьютера в реальном времени можно использовать представленный ниже Python-скрипт.
import serial
import matplotlib.pyplot as plt
import numpy as np


arduino = serial.Serial("/dev/ttyUSB0", 9600)
plt.ion()
counter = 0
x = []

while True:
    data = arduino.readline()
    sep  = data.split()
    if len(sep)==0:
        continue
    x.append(float(sep[0]))

    plt.clf()
    plt.xlim(0, 100)
    plt.ylim(0, 5)
    plt.title("Analog Data")
    plt.grid(True)
    plt.ylabel("U, V")
    plt.plot(x, 'ro-')
    plt.pause(0.001)
    plt.draw()

    counter += 1
    if(counter>100):
        x.pop(0)

arduino.close()

В случае, когда необходимо уменьшить диапазон напряжений на аналоговых входах можно использовать специальную команду analogReference() для задания верхнего предела напряжений +1.1 В или использование внешнего опорного напряжения, подаваемого на контакт AREF.
// ТОЛЬКО ДЛЯ ПЛАТФОРМЫ ARDUINO!

// Объявляем переменную для хранения результатов измерений напряжения
int sensorValue = 0;

void setup() {

    // Инициализируем последовательный интерферс на скорости 9600 бод
    Serial.begin(9600);
    
    // Задаем верхнюю границу диапазона измеряемых напряжений
    //-------------------------------------------------
    // Значение  Описание
    //-------------------------------------------------
    // DEFAULT   +5.0 В
    // INTERNAL  +1.1 В
    // EXTERNAL  задается напряжением на контакте AREF
    //-------------------------------------------------
    analogReference(INTERNAL);
}

void loop() {

    for(int i=1; i<9; i++) {
        sensorValue = analogRead(i);
        Serial.print(sensorValue*1.1/1023, 2);
        Serial.print("\t");
    }
    Serial.println();
    delay(500);
}
Частота дискретизации АЦП определяется как одна 128-ая часть от частоты микроконтроллера (обычно равной 16 МГц). При этом, для аналого-цифрового преобразования требуется 13 циклов, то есть частота дискретизации составляет:

f = 16000000 / 128 / 13 = 9615 Гц

Значение делителя по умолчанию равно 128, но может быть изменено программно на 64, 32 или 16, но при этом разрешение может незначительно ухудшится. Зависимость частоты дискретизации от значения делителя представлены в таблице.

ДелительЧастота дискретизации, Гц
1289615
6419230
3238461
1676923


// Массивы для хранения данных измерений
unsigned long start_times[100];
unsigned long stop_times[100];
unsigned long values[100];
 
 
//-------------------------------------
// Делитель  Частота дискретизации, Гц
//-------------------------------------
// 16        76923
// 32        38461
// 64        19230
// 128       9615
// Определяем несколько делителей частоты микропроцессора:
const unsigned char PS_16  = (1 << ADPS2);
const unsigned char PS_32  = (1 << ADPS2) | (1 << ADPS0);
const unsigned char PS_64  = (1 << ADPS2) | (1 << ADPS1);
const unsigned char PS_128 = (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0);

// Определяем несколько испочников опорного напряжения
// (+5 В, +2.56 В или внешний источник, подключенный к контакту AREF)
const unsigned char VREF_500  = (1 << REFS0);
const unsigned char VREF_256  = (1 << REFS0) | (1 << REFS1);
const unsigned char VREF_AREF = 0;


// Начальные настройки
void setup() {
 
    // Задаем скорость последовательного порта
    Serial.begin(9600);
 
    // Переключаем аналоговый контакт 2 на вход АЦП
    pinMode(2, INPUT);
 
    // Стираем биты делителя 128, установленного библиотекой Arduino
    ADCSRA &= ~PS_128;
 
    // Задаем делитель (PS_16, PS_32, PS_64 или PS_128)
    ADCSRA |= PS_128;

    // Задаем источник опорного напряжения (VREF_500, VREF_256 или VREF_AREF)
    ADMUX |= VREF_500;
}


void loop() {
 
    // Порядковый номер измерения
    unsigned int i;
    // Начальное значение суммы всех напряжений
    float summa = 0;
 
    // Измеряем 100 раз напряжение на 2 контакте AVR контроллера
    for(i=0; i<100; i++) {
        // Записываем время до начала чтения АЦП, мкс
        start_times[i] = micros();
        // Читаем напряжение на 2 входе АЦП, [0...1023]
        values[i]      = analogRead(2);
        // Записываем время после окончания чтения АЦП, мкс
        stop_times[i]  = micros();
        // Суммируем все значения напряжения
        summa += values[i];
    }
    // Определяем среднее значение напряжения делением суммы на число измерений
    float mean = summa/100;

    // Выводим результаты в последовательный порт
    Serial.println("\n\n--- Results ---");
    for(i=0; i<100; i++) {
        Serial.print(values[i]);
        Serial.print("\t");
        Serial.print(values[i]*5.0/1023);
        Serial.print(" V\t");
        Serial.print(stop_times[i] - start_times[i]);
        Serial.print(" us\n");
    }
    Serial.println("\n--- Mean Values ---");
    Serial.print(mean, 1);
    Serial.print("\t");
    Serial.print(mean*5.0/1023, 3);
    Serial.println(" V");
    Serial.println("-------------------\n");

    // Задержка 5 с
    delay(5000);
}

Передача данных измерений на сервер

Создаем аккаунт на сервере Thingspeak.com и регистрируем так называемый канал данных, через который будет осуществляться запись данных в облачное хранилище и чтение данных.

#include <SoftwareSerial.h>

// Пишем номера цифровых каналов для передачи и получения сигналов с esp8266
SoftwareSerial esp(10,11);

// Пишем ключ для подключения к каналу сервера thingspeak.com
String writeAPIKey = "XXXXXXXXXXXXXXXXXXXXX";

void setup() {
    // Задаем скорость последовательного порта
    Serial.begin(115200);
    // Инициализация модуля esp
    esp.begin(115200);
    delay(2000);
    //сбрасываем все навтройки esp
    esp.println("AT+RST\r\n");
    espdo(2000);
    // Задаём есп режим станции
    esp.println("AT+CWMODE=1");
    espdo(2000);
    // Устанавливаем параметры для подключения
    esp.println("AT+CWJAP=\"XXXXXXXXXX\",\"XXXXXXXXXXX\"");
    espdo(5000);
    // Разрешаем ему лишь одно подключение
    esp.println("AT+CIPMUX=0");
    espdo(2000);
}

void loop() {
    // измеряем напряжение на контакте А0
    int sensorValue = analogRead(A0);
    if(sensorValue>0) {
        // передаем измеренное значение в измеренный порт
        Serial.println(sensorValue);
        // задержка на 1 секундy:
        delay(1000);
        String tsData = "field1=";
        tsData += sensorValue;
        Serial.print(tsData);
        // Формируем строку запроса
        String cmd = "POST /update HTTP/1.1\n";
        cmd += "Host: api.thingspeak.com\n";
        cmd += "Connection: close\n";
        cmd += "X-THINGSPEAKAPIKEY: "+writeAPIKey+"\n";
        cmd += "Content-Type: application/x-www-form-urlencoded\n";
        cmd += "Content-Length: ";
        cmd += tsData.length();
        cmd += "\n\n";
        cmd += tsData;
        // Подключаемся к серверу
        esp.println("AT+CIPSTART=\"TCP\",\"184.106.153.149\",80");
        espdo(3000);
        // Передаём данные
        esp.print("AT+CIPSEND=");
        esp.println( cmd.length() );
        espdo(3000);
        esp.println(cmd);
        espdo(20000);
    }
}

void espdo(long time) {
    delay(time);
    while(esp.available() > 0) {
        Serial.write(esp.read());
    }
}

    

Данные измерений можно увидеть на странице:
https://thingspeak.com/channels/141872/

Или перейти по QR-коду:

Полезная информация