查看原文
其他

DIY云端情书打印机(基于腾讯定制开发板)

杨源鑫 嵌入式应用研究院 2022-09-10

如下图所示,这就是腾讯定制开发板EVB_G0开发板啦:

关于这款开发板的介绍,详细资料可以参考以下TencentOS官方公众号的两篇DIY作品的文章:

TencentOS Tiny手把手教您自制智能甲醛监测仪

基于TencentOS Tiny&腾讯连连的炫酷物联网智能灯,你值得拥有!

1、DIY项目展示

为了DIY这个简单的小项目,我把它改造成下面这个样子,也加了不少模块,有些功能并没有实现:

先来看下目前的基本功能演示:

2、项目整体框架

3、项目软件主要逻辑

3.1、STM32CubeMX配置

这个模板直接拷贝TencentOS tiny官方内核BSP包里的,然后我自己再加了一些别的配置进去做了下简单的修改。

3.2、云端与设备交互逻辑

在没有云端指令下发时,设备会定时上传当前环境的光强数值到腾讯云平台,当有云端指令下发时,主要分为以下几种情况:

  • 控制E53_SC1模组上的电灯亮灭
  • 控制外接RGB浪漫彩灯定时闪烁
  • 控制热敏打印机打一周(周一~周日)情书
  • 控制热敏打印机打印云端下发的字符串
    根据云平台规定,编写如下Json数据模板脚本:
{
  "version""1.0",
  "profile": {
    "ProductId""WONYDFWVJO",
    "CategoryId""539"
  },
  "properties": [
    {
      "id""power_switch",
      "name""电灯开关",
      "desc""控制灯光",
      "required"true,
      "mode""rw",
      "define": {
        "type""bool",
        "mapping": {
          "0""关灯",
          "1""开灯"
        }
      }
    },
    {
      "id""power_switch1",
      "name""浪漫彩灯",
      "desc""控制彩灯",
      "required"true,
      "mode""rw",
      "define": {
        "type""bool",
        "mapping": {
          "0""关彩灯",
          "1""开彩灯"
        }
      }
    },
    {
      "id""lx",
      "name""光照强度",
      "desc""光照强度",
      "mode""r",
      "define": {
        "type""int",
        "min""0",
        "max""20000",
        "start""0",
        "step""1",
        "unit""lx"
      },
      "required"false
    },
    {
      "id""week",
      "name""一周情书",
      "desc""一周的情书",
      "mode""rw",
      "define": {
        "type""enum",
        "mapping": {
          "0""Monday",
          "1""Tuesday",
          "2""Wednesday",
          "3""Thursday",
          "4""Friday",
          "5""Saturday",
          "6""Sunday"
        }
      },
      "required"false
    },
    {
      "id""cloud_print",
      "name""云打印机",
      "desc""通过云端下发字符串控制打印机打印数据",
      "mode""rw",
      "define": {
        "type""string",
        "min""0",
        "max""2048"
      },
      "required"false
    }
  ],
  "events": [],
  "actions": []
}

在腾讯云平台以及腾讯连连小程序上数据模板展示效果如下:

光强属于上行指令:

memset(report_topic_name, 0, sizeof(report_topic_name));
size = snprintf(report_topic_name, TOPIC_NAME_MAX_SIZE, "$thing/up/property/%s/%s", product_id, device_name);
if (size < 0 || size > sizeof(report_topic_name) - 1)
{
  printf("pub topic content length not enough! content size:%d  buf size:%d", size, (int)sizeof(report_topic_name));
}

当设备上线时,平台会收到上行数据,定时显示当前环境下的光强数值:

while (1)
{
  // 读取光照强度,数据存放在 e53_value
  e53_value = e53_scl_read_data();
  printf("e53 value %.0f lx\r\n", e53_value);
  memset(e53_str, 0, sizeof(e53_str));
  sprintf(e53_str, "%.0f lx(lm/m2)", e53_value);
  OLED_Clear();
  OLED_ShowString(0, 0, (uint8_t*)"TencentOS-tiny", 16);
  OLED_ShowString(0, 2, (uint8_t*)e53_str, 16);
  // 1. 构造上报的JSON
  memset(payload, 0, sizeof(payload));
  snprintf(payload, sizeof(payload), REPORT_LX_DATA_TEMPLATE, e53_value);
  // 2. 向数据模板 topic 发布光照强度信息
  if (tos_tf_module_mqtt_pub(report_topic_name, QOS0, payload) != 0)
  {
      printf("module mqtt pub fail\n");
      break;
  }
  else
  {
      printf("module mqtt pub success\n");
  }
  tos_sleep_ms(7000);
}

除了光强以外,其余的参数属性属于下行指令,当平台下发指令时,会触发MQTT做数据订阅功能时的回调函数:default_message_handler,这部分在初始化的时候就设计好了,代码如下:

// 5. 订阅数据模板 topic
size = snprintf(report_reply_topic_name, TOPIC_NAME_MAX_SIZE, "$thing/down/property/%s/%s", product_id, device_name);

if (size < 0 || size > sizeof(report_reply_topic_name) - 1)
{
   printf("sub topic content length not enough! content size:%d  buf size:%d", size, (int)sizeof(report_reply_topic_name));
}

if (tos_tf_module_mqtt_sub(report_reply_topic_name, QOS0, default_message_handler) != 0)
{
   printf("module mqtt sub fail\n");
}
else
{
   printf("module mqtt sub success\n");
}

该函数会依次对下发的指令进行匹配处理,下发的指令格式是Json,这里我们采用CJson来对数据进行解析:

/***************************************************************
* 函数名称: default_message_handler
* 说    明: IoT Explorer下行数据处理
***************************************************************/
static void default_message_handler(mqtt_message_t* msg)
{
    cJSON *root;
    cJSON *params;
    cJSON *method;
    cJSON *week_love;
    cJSON *power_switch;
    cJSON *power_switch1;
    cJSON *cloud_print ;
    root = cJSON_Parse(msg->payload + 1);
    if (!root)
    {
        printf("Invalid json root\r\n");
        return;
    }
    method = cJSON_GetObjectItem(root, "method");
    if (!method)
    {
        printf("Invalid json method\r\n");
        cJSON_Delete(root);
        return;
    }
    if (0 != strncmp(method->valuestring, "control", sizeof("control") - 1))
    {
        cJSON_Delete(root);
        return;
    }
    //获取参数
    params = cJSON_GetObjectItem(root, "params");
    if (!params)
    {
        printf("Invalid json params\r\n");
        cJSON_Delete(root);
        return;
    }
    //获取power_switch参数
    power_switch = cJSON_GetObjectItem(params, "power_switch");
    if (power_switch)
        iot_explorer_handle_power_switch(power_switch->valueint);
    //获取power_switch1参数
    power_switch1 = cJSON_GetObjectItem(params, "power_switch1");
    if (power_switch1)
        iot_explorer_handle_power_switch1(power_switch1->valueint);
    //获取week参数
    week_love = cJSON_GetObjectItem(params, "week");
    if(week_love)
        iot_explorer_handle_week(week_love->valueint);
    //获取cloud_print参数
    cloud_print = cJSON_GetObjectItem(params, "cloud_print");
    if(cloud_print)
        iot_explorer_handle_string(cloud_print->valuestring);
    cJSON_Delete(root);
}

参数功能如下:

  • power_switch 参数

控制E53模块上的电灯

调用iot_explorer_handle_power_switch函数处理。

/***************************************************************
* 函数名称: iot_explorer_handle_power_switch
* 说    明: 根据power switch控制开关
***************************************************************/
static void iot_explorer_handle_power_switch(int power_switch)
{
    if (0 == power_switch)
    {
        printf("iot-explorer close the light\r\n");
        LED_CLOSE;
        HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET);
        OLED_Clear();
        OLED_ShowString(0, 0, (uint8_t*)"Close Led", 16);
    }
    else
    {
        printf("iot-explorer open the light\r\n");
        LED_OPEN;
        HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET);
        OLED_Clear();
        OLED_ShowString(0, 0, (uint8_t*)"Open Led", 16);
    }
}
  • power_switch1 参数

控制彩灯

调用iot_explorer_handle_power_switch1函数处理。

/***************************************************************
* 函数名称: iot_explorer_handle_power_switch
* 说    明: 根据power switch控制彩灯开关
***************************************************************/
static void iot_explorer_handle_power_switch1(int power_switch)
{
    if (0 == power_switch)
    {
        printf("iot-explorer close the color light\r\n");
        Controld_Color_LED = 0 ;
        OLED_Clear();
        OLED_ShowString(0, 0, (uint8_t*)"Close Color Led", 16);
    }
    else
    {
        printf("iot-explorer open the color light\r\n");
        Controld_Color_LED = 1 ;
        OLED_Clear();
        OLED_ShowString(0, 0, (uint8_t*)"Open Color Led", 16);
    }
}
  • week 参数

控制热敏打印机打印一周的某一天的情书

调用iot_explorer_handle_week函数处理。

/***************************************************************
* 函数名称: iot_explorer_handle_week
* 说    明: 根据week控制打印机
***************************************************************/
static void iot_explorer_handle_week(int week)
{
  //略...
}
  • cloud_print 参数

控制热敏打印机打印下行字符串

调用iot_explorer_handle_string函数处理。

/***************************************************************
* 函数名称: iot_explorer_handle_string
* 说    明: 根据string控制打印机打印字符串
***************************************************************/
static void iot_explorer_handle_string(char *str)
{
    /*收到情书,则蜂鸣器鸣叫3声进行提醒*/
    uint8_t count = 0 ;
    for(count = 0 ; count < 6 ; count++)
    {
        HAL_GPIO_TogglePin(BUZZER_GPIO_Port, BUZZER_Pin);
        tos_task_delay(500);
    }

    HAL_GPIO_WritePin(BUZZER_GPIO_Port, BUZZER_Pin, GPIO_PIN_RESET);
    printf("云端数据:%s\n", str);
    OLED_Clear();
    OLED_ShowString(0, 0, (uint8_t*)str, 16);
}

3.3、热敏打印机模块

热敏打印机用的是上次参加腾讯连连比赛MC-EH205:

【腾讯连连IoT开发大赛】基于TencentOS tiny云打印机&智能达特甲醛探测系统

之前MCU用的是M4的架构,但厂家提供了M3对应的lib和头文件却可以直接被编译兼容,所以驱动起来非常简单,只需要包含对应的库然后调用对应的函数即可:

而本次用的是M0的架构MCU,编译的时候提示不兼容,所以lib也就不适用了,但是没关系,直接参考指令集手册来驱动打印机模块也是可以的。

根据项目要求,我们需要提供发送指令和发送数据的两个函数:

//发送单个字节给打印机
void Printf_send_byte(uint8_t data)
{
    /*调度器上锁*/
    tos_knl_sched_lock();
    HAL_UART_Transmit(&huart3, &data, 1, 1000);

    while(__HAL_UART_GET_FLAG(&huart3, UART_FLAG_TXE) != SET);

    /*调度器解锁*/
    tos_knl_sched_unlock();
}


//发送字符串给打印机
static void Printf_send_command(char* str)
{
    /*调度器上锁*/
    tos_knl_sched_lock();
    while(*str)
    {
        Printf_send_byte(*str);
        str++;
    }
    /*调度器解锁*/
    tos_knl_sched_unlock();
}

模块上电需要发送初始化指令:

/*初始化打印设备*/
void init_print_device(void)
{
 Printf_send_byte(0x1B);
 Printf_send_byte(0x40);

手册说明如下:

接下来就可以愉快的发送字符串给模块打印纸条了,如果发送的字符串显示在纸条上需要对齐的话,则需要根据需求发送文本对齐指令,然后再发送字符串,这样的话打印出来的效果就会按照设定要求进行:

如下所示:

模块的可玩性很高,指令集也兼容了市面上常见的热敏打印机,可以开发出很多有趣好玩的产品。

4、项目开源地址

本节代码已同步到码云的代码仓库中,获取方法如下:

码云仓库:

https://gitee.com/morixinguan/personal-open-source-project/tree/master/6.Love_Letter_printer

获取项目方法:

git clone https://gitee.com/morixinguan/personal-open-source-project.git

我还将之前做的一些项目以及练习例程在近期内全部上传完毕,与大家一起分享交流,如果有任何问题或者对该项目感兴趣,欢迎加我微信:morixinguan一起交流学习。

往期精彩

一个超酷的开源uHand2.0机械手掌项目

开源作品:基于RT-Thread 智慧农业监测系统产品级开发

从SD卡拷贝UI资源到QSPI Flash文件系统(仿串口终端显示拷贝过程)

开源作品:基于TencentOS tiny英国达特甲醛探测仪产品级开发(二)

觉得本次分享的文章对您有帮助,随手点[在看]并转发分享,也是对我的支持。

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存